Hi,
My bcrypt module install fails in docker.
I am using docker for a node application the following is my docker file:
FROM node:10.16.2-alpine
# Create app directory
WORKDIR /app
COPY package*.json /app/
# Installs everything from package.json
# RUN npm install
# Will copy all files except node_modules
COPY . .
RUN apk --no-cache --update --virtual build-dependencies add \
tzdata \
build-base python \
&& npm install \
&& apk del build-dependencies
ENV TZ Australia/Sydney
# even though port is being exposed it will
# be overridden by docker-compose port
EXPOSE 4000
CMD [ "npm", "start" ]
I have followed instructions in some other threads, mainly this one:
https://github.com/nodejs/docker-node/issues/282
However I get a huge dump as soon as source compilation starts during docker-compose up.
Below is the dump for reference:
> [email protected] install /app/node_modules/bcrypt
> node-pre-gyp install --fallback-to-build
node-pre-gyp WARN Using request for node-pre-gyp https download
node-pre-gyp WARN Tried to download(404): https://github.com/kelektiv/node.bcrypt.js/releases/download/v3.0.6/bcrypt_lib-v3.0.6-node-v64-linux-x64-musl.tar.gz
node-pre-gyp WARN Pre-built binaries not found for [email protected] and [email protected] (node-v64 ABI, musl) (falling back to source compile with node-gyp)
make: Entering directory '/app/node_modules/bcrypt/build'
CXX(target) Release/obj.target/bcrypt_lib/src/blowfish.o
CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt.o
CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt_node.o
In file included from ../src/bcrypt_node.cc:1:
../node_modules/nan/nan.h: In function 'void Nan::AsyncQueueWorker(Nan::AsyncWorker*)':
../node_modules/nan/nan.h:2232:62: warning: cast between incompatible function types from 'void (*)(uv_work_t*)' {aka 'void (*)(uv_work_s*)'} to 'uv_after_work_cb' {aka 'void (*)(uv_work_s*, int)'} [-Wcast-function-type]
, reinterpret_cast<uv_after_work_cb>(AsyncExecuteComplete)
^
In file included from ../node_modules/nan/nan.h:53,
from ../src/bcrypt_node.cc:1:
../src/bcrypt_node.cc: At global scope:
/root/.node-gyp/10.16.2/include/node/node.h:573:43: warning: cast between incompatible function types from 'void (*)(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)' {aka 'void (*)(v8::Local<v8::Object>)'} to 'node::addon_register_func' {aka 'void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)'} [-Wcast-function-type]
(node::addon_register_func) (regfunc), \
^
/root/.node-gyp/10.16.2/include/node/node.h:607:3: note: in expansion of macro 'NODE_MODULE_X'
NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
^~~~~~~~~~~~~
../src/bcrypt_node.cc:378:1: note: in expansion of macro 'NODE_MODULE'
NODE_MODULE(bcrypt_lib, init);
^~~~~~~~~~~
In file included from /root/.node-gyp/10.16.2/include/node/node.h:63,
from ../node_modules/nan/nan.h:53,
from ../src/bcrypt_node.cc:1:
/root/.node-gyp/10.16.2/include/node/v8.h: In instantiation of 'void v8::PersistentBase<T>::SetWeak(P*, typename v8::WeakCallbackInfo<P>::Callback, v8::WeakCallbackType) [with P = node::ObjectWrap; T = v8::Object; typename v8::WeakCallbackInfo<P>::Callback = void (*)(const v8::WeakCallbackInfo<node::ObjectWrap>&)]':
/root/.node-gyp/10.16.2/include/node/node_object_wrap.h:84:78: required from here
/root/.node-gyp/10.16.2/include/node/v8.h:9502:16: warning: cast between incompatible function types from 'v8::WeakCallbackInfo<node::ObjectWrap>::Callback' {aka 'void (*)(const v8::WeakCallbackInfo<node::ObjectWrap>&)'} to 'Callback' {aka 'void (*)(const v8::WeakCallbackInfo<void>&)'} [-Wcast-function-type]
reinterpret_cast<Callback>(callback), type);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/root/.node-gyp/10.16.2/include/node/v8.h: In instantiation of 'void v8::PersistentBase<T>::SetWeak(P*, typename v8::WeakCallbackInfo<P>::Callback, v8::WeakCallbackType) [with P = Nan::ObjectWrap; T = v8::Object; typename v8::WeakCallbackInfo<P>::Callback = void (*)(const v8::WeakCallbackInfo<Nan::ObjectWrap>&)]':
../node_modules/nan/nan_object_wrap.h:65:61: required from here
/root/.node-gyp/10.16.2/include/node/v8.h:9502:16: warning: cast between incompatible function types from 'v8::WeakCallbackInfo<Nan::ObjectWrap>::Callback' {aka 'void (*)(const v8::WeakCallbackInfo<Nan::ObjectWrap>&)'} to 'Callback' {aka 'void (*)(const v8::WeakCallbackInfo<void>&)'} [-Wcast-function-type]
SOLINK_MODULE(target) Release/obj.target/bcrypt_lib.node
COPY Release/bcrypt_lib.node
COPY /app/node_modules/bcrypt/lib/binding/bcrypt_lib.node
TOUCH Release/obj.target/action_after_build.stamp
make: Leaving directory '/app/node_modules/bcrypt/build'
I would appreciate any help.
These are all just compile warnings, not errors, at the bottom you have a successful bcrypt_lib.node that can be loaded as an addon. If you need the warnings to go away then you'll have to take that up with the node-bcrypt folks (or redirect the output to /dev/null if you _really_ don't want to see it).
Thanks for the reply @rvagg however bcrypt import doesnt work in koa.js even after the successful bcrypt_lib.node compilation. For the time I am using an all javascript version of bcrypt with zero dependancies called bcryptjs to get past the issue.
so, try running your container with bash and do a node -e require("/app/node_modules/bcrypt/lib/binding/bcrypt_lib.node") and what does it say? And what does node -e require("/app/node_modules/bcrypt/") say? do these return errors?
@rvagg I have given the commands below and they dont give any error:
node -e 'require("./node_modules/bcrypt/lib/binding/bcrypt_lib.node")'
and
node -e 'require("./node_modules/bcrypt/")'
However when the actual node app runs ( which by the way is in typescript) the follwing shows up:
web_1 |
web_1 | > [email protected] start /app
web_1 | > npm run build:live
web_1 |
web_1 |
web_1 | > [email protected] build:live /app
web_1 | > nodemon --inspect=0.0.0.0:9229 -r ts-node/register -P ./tsconfig.json ./src/index.ts
web_1 |
web_1 | [nodemon] 1.18.11
web_1 | [nodemon] to restart at any time, enter `rs`
web_1 | [nodemon] watching: *.*
web_1 | [nodemon] starting `node --inspect=0.0.0.0:9229 -r ts-node/register ./src/index.ts`
web_1 | Debugger listening on ws://0.0.0.0:9229/13799f3e-3aa8-47fa-aae3-532657c50c7d
web_1 | For help, see: https://nodejs.org/en/docs/inspector
web_1 | createStore executed once
web_1 | Error: Error loading shared library /app/node_modules/bcrypt/lib/binding/bcrypt_lib.node: Exec format error
web_1 | at Object.Module._extensions..node (internal/modules/cjs/loader.js:807:18)
web_1 | at Module.load (internal/modules/cjs/loader.js:653:32)
web_1 | at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
web_1 | at Function.Module._load (internal/modules/cjs/loader.js:585:3)
web_1 | at Module.require (internal/modules/cjs/loader.js:692:17)
web_1 | at require (internal/modules/cjs/helpers.js:25:18)
web_1 | at Object.<anonymous> (/app/node_modules/bcrypt/bcrypt.js:6:16)
web_1 | at Module._compile (internal/modules/cjs/loader.js:778:30)
web_1 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
web_1 | at Module.load (internal/modules/cjs/loader.js:653:32)
web_1 | at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
web_1 | at Function.Module._load (internal/modules/cjs/loader.js:585:3)
web_1 | at Module.require (internal/modules/cjs/loader.js:692:17)
web_1 | at require (internal/modules/cjs/helpers.js:25:18)
web_1 | at Object.<anonymous> (/app/src/db/userApi.ts:5:16)
web_1 | at Module._compile (internal/modules/cjs/loader.js:778:30)
web_1 | [nodemon] app crashed - waiting for file changes before starting...
I do additionally install "@types/bcrypt" for type definitions as dev dependencies.
Yeah, that is a bit weird. Is it possible that you have multiple versions of node installed on this image? do you know what node that nodemon is finding?
If you run this manually inside the container: node -p 'process.execPath + "@" + process.version' it'll give you the path and the version of the executable. Perhaps you could change the nodemon startup command to nodemon -p 'process.execPath + "@" + process.version' and start your app and see if it prints the same thing? (I'm not sure nodemon will be happy with that, but you could try).
nodemon is not happy as you already suspected. I have however tried to make sure the version of node is the same in all places:
1 - my local machine. Using latest nvm and using 10.16.2 as default.
2 - docker file
3 - ran node -p 'process.execPath + "@" + process.version' in my app's docker image running in an interactive shell, and it does give me the same node.js version
So still on square one.
Unfortunately version isn't the only compatibility vector. Alpine uses musl libc, not glibc. Presumably you're using an official Alpine Docker image (from nodejs/docker-node, published as the official node images). They are using proper musl builds, so glibc isn't involved at all. But it's possible to install glibc and compile against it, or use standard Linux x64 binaries on Alpine that are compiled against glibc. If you compile an addon with a glibc node and load it in a musl node I think you'll see the problem you're experiencing. Same with the reverse. Hence the need to determine that the node invoking node-gyp for building the addon is the same that loads it.
Is it possible another node is getting installed on the image? Perhaps via nvm or some other means? afaik nodemon just finds whatever is in PATH but maybe it's getting it from somewhere else.
https://github.com/remy/nodemon#default-executables nodemon has the ability to manually specify the executable to be used. I also notice in https://github.com/remy/nodemon/blob/master/lib/config/defaults.js it has a ts default. Is it possible that it's invoking ts-node and this is a binary? I don't know enough about ts-node, maybe it's just a wrapper or maybe it's pulling in its own node binary?
Ping @nodejs/docker - this might be interesting for you and maybe someone's dealt with this before and has a better answer than me. Summary: compiling an addon inside a container isn't being loaded when the app is started Error: Error loading shared library. nodemon and TypeScript are involved here.
I will take a look! I seem to remember an issue with alpine and bcrypt though.
Closing due to inactivity, can reopen if necessary.
I'm running into the same issue while trying to build an image with https://github.com/laverdet/isolated-vm
I'm running into the same issue for npm dependencies that rely on node-gyp. Here's my current Dockerfile (I've configured it many different ways to no avail):
FROM node:alpine
WORKDIR "/app"
COPY ./package.json ./
RUN apk add python make gcc g++
RUN npm install
COPY . .
CMD ["npm", "run", "start"]
@danielcurtis Try using a specific Node.js version. I've been able to resolve it with:
FROM node:14-alpine
RUN apk add --no-cache g++ make python
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . ./
CMD ["npm", "run", "start"]
There seems to be an issue with Node 15: https://github.com/nodejs/node-gyp/issues/2245
Edit: Using
FROM node:15.1-alpine
RUN apk add --nocache python make g++
fixes it for me.