graphql-framework-experiment
graphql-framework-experiment copied to clipboard
Docker guide
What
-
Create a guide for working with docker
-
users should take advantage of
docker run -t
to give their dev mode a tty#556 / https://github.com/graphql-nexus/nexus/issues/556#issuecomment-612606699
-
Show how to make work with sigterm:
docker run --rm -e DEBUG=true -t t1 dev 832 ● nexus:dev start -- version: '0.20.0-next.41' 402 ● server listening -- url: 'http://localhost:4000' C
-
show suggested Dockerfile (until we maintain official Nexus images), e.g. for hello-world example:
ROM node:12 ORKDIR /project OPY package.json yarn.lock ./ UN yarn -s install OPY api tsconfig.json ./ UN yarn -s build NTRYPOINT ["yarn", "-s"] MD ["start"]
-
Show how to get working with Alpine
Any plans to support alpine version of the docker sometime in future? I know previously prisma had some issues with glibc. I managed to work it out using the below (still to optimise for layer caching), but an official support from alpine base image would be great.
FROM jeanblanchard/alpine-glibc:3.11
WORKDIR /app
COPY . ./
RUN apk add --no-cache --update nodejs npm \
&& SKIP_GENERATE="true" npm install \
&& npm run build \
&& npm prune --production \
&& apk del npm \
&& /bin/rm -rf /root/.cache /root/.config /root/.npm
ENV NODE_ENV production
EXPOSE 4000
CMD ["node", "node_modules/.build"]
Yeah supporting Alpine would be great I think. Adding it to the above list.
My 2¢ deployment, multistage, alpine DF:
FROM node:13 as base
RUN yarn global add node-gyp typescript@3 lerna
ENV SERVER_PKG=packages/server
##########################################################
FROM base as monorepo-base
WORKDIR /app
COPY yarn.lock package.json lerna.json ./
RUN yarn install --non-interactive --pure-lockfile
##########################################################
FROM monorepo-base as nexus-builder
WORKDIR /app/$SERVER_PKG
COPY $SERVER_PKG/package.json ./
RUN yarn install --non-interactive --pure-lockfile
COPY $SERVER_PKG/.env.example .
COPY $SERVER_PKG/tsconfig.json .
COPY $SERVER_PKG/prisma prisma
COPY $SERVER_PKG/api api
RUN yarn build
##########################################################
FROM node:13-alpine as server
ENV NODE_ENV=production
ENV SERVER_BUILD=/app/packages/server
WORKDIR $SERVER_BUILD
COPY --from=nexus-builder $SERVER_BUILD/package.json /app/yarn.lock ./
RUN yarn install --non-interactive --pure-lockfile --prod && yarn cache clean
COPY --from=nexus-builder ["/app/node_modules/@prisma/client", "node_modules/@prisma/client"]
COPY --from=nexus-builder $SERVER_BUILD/.env.example .
# Adjust for tsc outDir
COPY --from=nexus-builder $SERVER_BUILD/dist dist
CMD ["run", "start"]
ENTRYPOINT yarn
@kazazes do you see any value in a dedicated docker image for Nexus? Meaning, one that you can use in the FROM
line.
@jasonkuhrt with your suggested Dockerfile, I get the error
{"path":["nexus","tsconfig"],"context":{},"event":"Your tsconfig.json is invalid\n\n\u001b[91merror\u001b[0m\u001b[90m TS18003: \u001b[0mNo inputs were found in config file '/project/tsconfig.json'. Specified 'include' paths were '[\"src\"]' and 'exclude' paths were '[\"node_modules/.build\"]'.\n","level":60}
(I am using src
instead of api
, FYI)
@iherger can you share a repro?
@jasonkuhrt How much would it differ from what's posted above? Besides fixing nexus version?
@iherger make sure your package paths are correctly defined in the ENV
statements.
@kazazes I don't think very much, but not sure until we look at this issue more closely.
I think making a docker image for nexus would be a great way to get around the windows issues, as my company only uses Windows, so having a pre-made image would make the installation process much faster for me.
But for now I would be happy with just getting a guide on how to set up a docker project with Nexus.
@kazazes Can you link the project that this docker file is pulling from?
It's private. Is yours public? Happy to have a look.
Unfortunately mine is private as well, but I'm just trying to work through the logic of yours. If you have the time maybe just make a small server and show how you would implement it?
Just for reference I'm trying to upgrade several services I created almost a year ago to the Nexus framework, some services are Prisma and some are API interfaces. But my dev environment has to be Windows, hence the need to dockerize it. However this fits in with future strategies to dockerize all our services and deploy the to clusters anyway.
So seeing how you are doing it with Prisma would be very helpful to me.
Sure. Give me an hour.
@blazestudios23 here you go.
@kazazes Thanks a lot I'm looking it over, and I'm sure it will help me!!
When I use docker I can get "nexus dev" to work and start a dev server, but running: RUN npm run build CMD ["npm", "start"]
Results in the following error: "Error: Cannot find module '/usr/app/node_modules/.build'"
I'm just using this example: https://github.com/graphql-nexus/examples/tree/master/hello-world
With the following docker file:
# This stage installs our modules
FROM node:alpine
WORKDIR /usr/app
COPY ./package.json ./
RUN npm install
COPY ./ ./
RUN npm run build
# CMD ["npm","run", "dev"] # works
CMD ["npm", "start"] # doesn't work
EXPOSE 4000
Could you provide a repro? Did it work in the example repo above?
Here's the exact test repo I'm using: https://github.com/blazestudios23/docker-nexus-test
npm run test works but npm start does not.
Typescript was building to .nexus/build
. You can specify the output location in your tsconfig and adjust your start script to match.
diff --git a/package.json b/package.json
index efa4067..90615bd 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
},
"scripts": {
"clean": "rm -rf dist",
- "start": "node node_modules/.build",
+ "start": "node dist",
"build": "nexus build",
"generate": "nexus generate",
"dev": "nexus dev",
@@ -27,7 +27,7 @@
"preset": "ts-jest",
"testEnvironment": "node"
},
- "main": "index.js",
+ "main": "dist/index.js",
"directories": {
"test": "tests"
},
diff --git a/tsconfig.json b/tsconfig.json
index ac768be..e95141e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,8 @@
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
- "rootDir": "api"
+ "rootDir": ".",
+ "outDir": "dist"
},
- "include": ["api"]
+ "include": ["."]
}
Easiest way to debug things like this is to run your own cmd in the docker image instead of the CMD line.
$ docker run -p 4000:4000 --rm nt **ls node_modules/.build**
ls: node_modules/.build: No such file or directory
$ docker run -p 4000:4000 --rm nt ls dist
api
index.js
node_modules
@kazazes Thanks on the debugging tips those are helpful. I'm not a total docker Ninja yet.
I actually tried that .nexus/build it didn't work either. And I tried to find it in the node_modules folder, but didn't see it at all.
@kazazes So it's creating a .nexus file and then not putting anything in it during the build process.
/usr/src/app/node_modules # cd .nexus
/usr/src/app/node_modules/.nexus # ls -a
. .. cache.tsbuildinfo
There is no .build file as it shows in the example.
node_modules/.nexus
should contain the ts build cache.
$PROJECT_ROOT/.nexus/build/api
would contain the build output in a project that doesn't specify an outDir, not the node_module directory.
OK their example project was completely off, it was in: .nexus/build/api
First off the fact that I'm running it as a Docker Image made it harder to find, second putting the build in the .nexus file made it harder to find, as files . are hidden when you do ls, you have to use the -a flag. But was thrown a red hearing by their example which put the files in the wrong directory.
There is literally nothing worse than a Hello World Example that doesn't even work.
@blazestudios23 this version of nexus is iterating very quickly, but I feel ya. I updated the docs in #939.
So I'm now trying to get the testing library working in the simple example repo that Nexus made. But I get an error from this code:
import { createTestContext, TestContext } from "nexus/testing"
let ctx = {} as TestContext
beforeAll(async () => {
const testContext = await createTestContext()
Object.assign(ctx, testContext)
await ctx.app.start()
})
afterAll(async () => {
await ctx.app.stop()
})
it("works", async () => {
expect(
await ctx.app.query(`
query {
users {
id
}
}
`)
).toMatchInlineSnapshot(`
Object {
"users": Array [
Object {
"id": "1",
},
],
}
`)
})
The error says that ctx.app,query doesn't exist. What's the work around for this?
@blazestudios23 Looks like we missed mentioning this in one of our releases. The API for app client changed, see https://nexusjs.org/api/nexus-testing#i-testcontext.
ctx.client.send(...)
I've been trying various formulas here to get my instance up and running - both the simple one by OP @jasonkuhrt and the optimized multistage by noble sir @kazazes
Having issues here:
#25 0.895 $ nexus build
#25 2.354 {"event":"get used plugins","level":3,"path":["nexus","build"]}
#25 2.818 {"event":"failed to get used plugins","level":6,"path":["nexus"],"context":{"error":{"code":"MODULE_NOT_FOUND","requireStack":["/app/packages/server/api/FirebaseAdmin/FirebaseAdmin.ts","/app/packages/server/api/graphql/firebase/firebase_API.ts","/app/node_modules/nexus/dist/runtime/start/dev-runner.js","/app/node_modules/nexus/dist/runtime/start/index.js","/app/node_modules/nexus/dist/lib/reflection/reflect.js","/app/node_modules/nexus/dist/lib/plugin/worktime.js","/app/node_modules/nexus/dist/cli/commands/create/app.js","/app/node_modules/nexus/dist/cli/commands/create/index.js","/app/node_modules/nexus/dist/cli/commands/index.js","/app/node_modules/nexus/dist/cli/main.js"]}}}
EDIT: Got everything running fine now... the MODULE_NOT_FOUND was confusing me because that's all I was able to see outside of the container so I thought that the errors were from dependencies for the nexus plugins I was using... but indeed the problem was on my end.
If someone else stumbles upon similar issues to debug I just added an intermediate entrypoint as follows:
ENTRYPOINT ["/bin/bash"]
... and then ran the yarn -s build
inside the container, and was able to see much more verbose complaints about the missing modules (pretty general advise for Docker)
Here's the file I ultimately used (derived from https://github.com/kazazes/nexus-prisma-docker but minor modification using the newer .nexus/build
path)
##########################################################
# Setup a base image to build other packages from. #
# Only work as 'node' user with uid and gid 1000. #
##########################################################
FROM node:14 as monorepo-base
WORKDIR /app
ENV SERVER_PKG=packages/server
#ENV FRONTEND_PKG=packages/frontend
RUN mkdir -p $SERVER_PKG $FRONTEND_PKG \
&& chown -R 1000:1000 /app \
&& chmod -R 700 /app
USER node
WORKDIR /app
# Make the yarn cache to a writeable location, install node-gyp
RUN yarn config set cache-folder $HOME/.yarn-cache
RUN yarn global add --silent node-gyp
COPY --chown=node yarn.lock package.json lerna.json ./
##########################################################
# Build the API server to /app/packages/server/dist #
##########################################################
FROM monorepo-base as nexus-builder
WORKDIR /app/$SERVER_PKG
RUN touch .env
COPY --chown=node $SERVER_PKG/package.json ./
RUN yarn install --silent --non-interactive --pure-lockfile
#COPY --chown=node $SERVER_PKG/.env.example .
COPY --chown=node $SERVER_PKG/tsconfig.json .
COPY --chown=node $SERVER_PKG/prisma prisma
COPY --chown=node $SERVER_PKG/api api
#RUN export $(cat .env.example) && yarn build
RUN yarn -s build
##########################################################
# Build a deployment image with the -slim variant, only #
# including the necessary files from nexus-builder #
##########################################################
FROM node:14-slim as server
ENV SERVER_BUILD=/app/packages/server
ENV NODE_ENV=production
RUN mkdir -p $SERVER_BUILD && chown -R 1000:1000 /app
WORKDIR $SERVER_BUILD
USER node
RUN yarn config set cache-folder $HOME/.yarn-cache
COPY --chown=node --from=nexus-builder $SERVER_BUILD/package.json /app/yarn.lock ./
RUN yarn install --silent --non-interactive --pure-lockfile --prod && yarn cache clean
COPY --chown=node --from=nexus-builder ["/app/node_modules/@prisma/client", "node_modules/@prisma/client"]
#COPY --chown=node --from=nexus-builder $SERVER_BUILD/.env.example .
COPY --chown=node --from=nexus-builder $SERVER_BUILD/.nexus/build dist
CMD ["node", "dist/index.js"]
HEALTHCHECK --interval=60s --timeout=10s --start-period=30s \
CMD curl -f http://localhost:4000/graphql || exit 1
this docker recipe doesn't seem to work with the recent versions of nexus and plugins:
{
"nexus": "^0.26.1",
"nexus-plugin-jwt-auth": "^1.3.1",
"nexus-plugin-prisma": "^0.17.0",
"nexus-plugin-shield": "^0.2.0"
}
I used the example found here: https://github.com/graphql-nexus/examples/tree/master/plugins-prisma-and-jwt-auth-and-shield
I got it to successfully build and run on my local machine with nexus build
and node .nexus/build/api
and modified the Dockerfile
to fit that new setup to build the image:
##########################################################
# Setup a base image to build other packages from. #
# Only work as 'node' user with uid and gid 1000. #
##########################################################
FROM node:14 as monorepo-base
WORKDIR /app
#ENV FRONTEND_PKG=packages/frontend
RUN chown -R 1000:1000 /app \
&& chmod -R 700 /app
USER node
WORKDIR /app
# Make the yarn cache to a writeable location, install node-gyp
RUN yarn config set cache-folder $HOME/.yarn-cache
RUN yarn global add --silent node-gyp
COPY --chown=node yarn.lock package.json ./
##########################################################
# Build the API server to /app/packages/server/dist #
##########################################################
FROM monorepo-base as nexus-builder
WORKDIR /app
RUN touch .env
COPY --chown=node package.json ./
RUN yarn install --silent --non-interactive --pure-lockfile
#COPY --chown=node .env.example .
COPY --chown=node tsconfig.json .
COPY --chown=node prisma prisma
COPY --chown=node api api
#RUN export $(cat .env.example) && yarn build
RUN yarn -s build
##########################################################
# Build a deployment image with the -slim variant, only #
# including the necessary files from nexus-builder #
##########################################################
FROM node:14-slim as server
ENV NODE_ENV=production
RUN mkdir /app && chown -R 1000:1000 /app
WORKDIR /app
USER node
RUN yarn config set cache-folder $HOME/.yarn-cache
COPY --chown=node --from=nexus-builder /app/package.json /app/yarn.lock ./
RUN yarn install --silent --non-interactive --pure-lockfile --prod && yarn cache clean
COPY --chown=node --from=nexus-builder ["/app/node_modules/@prisma/client", "node_modules/@prisma/client"]
#COPY --chown=node --from=nexus-builder .env.example .
COPY --chown=node --from=nexus-builder /app/.nexus/build dist
CMD ["node", "dist/api"]
HEALTHCHECK --interval=60s --timeout=10s --start-period=30s \
CMD curl -f http://localhost:4000/graphql || exit 1
with the following docker-compose.yml
version: "3.7"
services:
server:
build:
context: .
dockerfile: Dockerfile
container_name: nexus-prisma-docker
restart: unless-stopped
init: true
ports:
- 4000:4000
I get an error relating to permission being denied on port 80
of all things:
✕ app uncaughtException
| error Error: listen EACCES: permission denied 0.0.0.0:80
| at Server.setupListenHandle [as _listen2] (net.js:1299:21)
| at listenInCluster (net.js:1364:12)
| at Server.listen (net.js:1450:7)
| at /app/dist/node_modules/nexus/dist/lib/utils/index.js:359:16
| at new Promise (<anonymous>)
| at Object.httpListen (/app/dist/node_modules/nexus/dist/lib/utils/index.js:358:12)
| at Object.start (/app/dist/node_modules/nexus/dist/runtime/server/server.js:79:31)
| at Object.start (/app/dist/node_modules/nexus/dist/runtime/app.js:72:43)
| at Object.<anonymous> (/app/dist/api/index.js:28:17)
| at Module._compile (internal/modules/cjs/loader.js:1256:30) {
| code: 'EACCES',
| errno: -13,
| syscall: 'listen',
| address: '0.0.0.0',
| port: 80
| }
do you guys have any idea what is causing this issue?