Closed xzyfer closed 7 years ago
Hey, I'm looking into getting this done. I'm working off a version of the Dockerfile you linked. Still not sure how to detect Alpine as the current OS but I haven't thought too much about it yet.
@amrocha currently we detect the underlying OS using process.platform
. This however return linux
Alpine. What we need is a way to differentiate Alpine from other linux distributions. This way we can download an alpine specific binary when installing rather than the generic linux binary.
There's some background information https://github.com/sass/node-sass/issues/756 where something similar was previously discussed to distinguish between different FreeBSD binaries.
Can somebody drop the contents of the process
node variable when running node on alpine? I don't think nodejs.org provides the Alpine-based build: what kind of build are people using to run node on that platform?
@saper I've asked for help from the Twitterspehere
So here's where I am:
What I'm doing right now is downloading the Node source and building it for every single version node-sass supports, but I'm having internet speed issues at home right now so I can only really get any progress done on this when I'm at work.
Thanks for looking at this. If you make the dockerfile a gist that'd be great. If alpine doesn't official support iojs then we possibly don't need to bother with those binaries. On 19 Jun 2016 2:47 AM, "Andre" notifications@github.com wrote:
So here's where I am:
- NVM doesn't support Alpine because it downloads pre-compiled node binaries, and Node doesn't provide Alpine binaries either;
- Alpine's package manager doesn't have IO.js or older versions of Node available as a package, and doesn't let you choose if you want the 32 bit or 64 bit version of a package so that doesn't work either;
What I'm doing right now is downloading the Node source and building it for every single version node-sass supports, but I'm having internet speed issues at home right now so I can only really get any progress done on this when I'm at work.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/sass/node-sass/issues/1589#issuecomment-226952416, or mute the thread https://github.com/notifications/unsubscribe/AAjZWOH9N-npSNazTnn9sUyAlJCiNaKBks5qNCE3gaJpZM4IzxUh .
Here's the dockerfile I'm using: https://gist.github.com/amrocha/56ffd0246a8bf31b72ae4667fbd1ea99
Alpine's package manager, apm, provides the following node versions:
I'll try asking the maintainer for the node package if there's a way of specifying if you want the 32 or 64 bit version, but it doesn't seem like there is.
@amrocha thank you for your work on this. If 32bit version are difficult to by I wouldn't bother until someone requests it.
I would simply stick to the versions of Node made available via apm.
@xzyfer that should be a lot easier, I'll see if I can get something together by the end of the day today
So I dug in a little bit more and turns out I was wrong about the versions of node Alpine provides. The versions above are all the versions that it has provided at some point in time, but you can only download the latest version.
Sorry about the confusion, I didn't expected that older versions would be unavailable.
@amrocha maybe there is a way to retrieve old package building information and re-build the node binaries yourself? For FreeBSD I have recovered old definitions and I keep using them to have older io.js binaries available.
@saper I could look into doing something similar, is there any difference between doing that and just building node from source though?
Building from source is fine :) On 22 Jun 2016 10:47 AM, "Andre" notifications@github.com wrote:
@saper https://github.com/saper I could look into doing something similar, is there any difference between doing that and just building node from source though?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sass/node-sass/issues/1589#issuecomment-227613792, or mute the thread https://github.com/notifications/unsubscribe/AAjZWKl3rzV1YowXitOQf2P8qc3MkOpEks5qOIYQgaJpZM4IzxUh .
Any progress on this? I think it would help a lot of people!
Sorry I haven't had much time to work on this. I want to build all the node versions first, and then compile the node-sass binaries for each of them, that way the node versions will be cached and you won't have to build them every time a new version is released. The Dockerfile I linked above works, but since there's no node version managers that work in Alpine I don't have an easy way to switch between versions. I was going to write my own simplistic implementation but just haven't had time to do it.
I'm not sure if I understand what you want to do, but can't you just use the same way like this image is doing to get different node versions?
That is pretty much what I'm doing, but I need to have multiple versions of node installed at the same time so I also need a way of managing versions.
It might be easier to not try to build multiple versions of node at the same time. Just create a Dockerfile that has the node-sass stuff in it and then starts with FROM mhart/alpine-node:X.X.X
. Then just change the version, build, and repeat for all the version numbers you want to support. I think on recent versions of docker, you can pass in environment variables to the build command so it could be as simple as something like NODE_VERSION=4.4.7 docker built -t node-sass:4.4.7 .
I agree, I think that would be the easier way forward...
Got some time to work on this again and wrote up a script that does what @jgallen23 suggested:
https://gist.github.com/amrocha/d7328f2d637745597fb5f46aad32f327
@xzyfer does this work for you?
Ouch creating new containers for each node version is rough. Not opposed to this. We'll be releasing a new version soonish so I'll see how we go.
I just realized that if we would could get node_module_version
from the config.gypi
(like https://github.com/nodejs/node/pull/7808) would make https://github.com/nodejs/node-gyp/pull/855 trivial and therefore we could probably cross-compile node-sass for all node engines using one node version without needing to install them one by one. I think it could make supporting alternative configurations like Alpine easier.
We could even use a cross-compiler as described on the musl wiki.
Which version of musl is being used now with alpine? 1.1.9?
An alternative would be to build static linux based sass binaries using musl, which would work 'everywhere'.
@Vektah we also need a runtime C++ library, hopefully the same as the one used by node itself. Have you tried to dlopen() statically linked node extension?
Is there a way around compiling node-sass bindings in the meantime?
Compiling node-sass increases my docker image build time from 2m to 7m which is very painful for continous deployments.
@abhishiv you can compile it, and host it. The use the documented EVN variable to instruct node-sass to fetch your binary during install.
@saper Sorry I missed your comment, I think the C++ runtime can stay dynamically linked until it causes problems, which is probably much less likely given it links to libc to talk to the kernel (I think?).
I'll take a look at this over the weekend but I think @spralle is pretty close, alpine already has a musl cross compiling gcc and g++ so we just need to tell it to statically link musl into a dynamic library right? (I doubt this will be easy...)
Have you tried to dlopen() statically linked node extension?
Nope, there could be problems. I'm hoping to be pleasantly surprised...
Hey, has there been any progress on this?
The musl in alpine doesn't seem to be able to generate a static binary, when passing --static
it fails to link with some missing headers.
Setting -static-libstdc++ -static-libgcc
builds an almost static binary, but still has a dependency on musl.
At this point it was about 2am, and I'm staring at the gcc / musl cross compiler docs. I think the alpine environment isn't quite complete for building static binaries. I don't know enough to know whats missing though.
@Vektah I appreciate your effort and persistence.
@Vektah looking at node's configure file they add -static only https://github.com/nodejs/node/blob/master/configure#L932
Maybe you can find some hints from there as they are building statically under alpine.
For those who need to play with node-sass, docker and most recent versions of node.js, my image supports it.
Issue from a user asking for support: https://github.com/cusspvz/node.docker/issues/9 Project link: https://github.com/cusspvz/node.docker
Will give it a go!
Any movement on this issue recently?
@cusspvz Thanks, but I prefer to use the official Node alpine favours that are offered (https://hub.docker.com/_/node/) as my base images. So this is still an issue.
Node Sass works with Alpine Linux however the native extension needs to be compiled. You'll need to make sure you've installed the prerequisite packages for compile native code.
apk add --no-cache python=2.7.12-r0 make gcc g++
The problem isn't compiling, it's about providing a precompiled binary for alpine. :)
As stated at the beginning, node-sass
does not detect Alpine platform and thus download a binary that fails. A first step could be to detect Alpine correctly, avoid downloading the binary and build from source. Second step, download a prebuilt binary :)
@joshuataylor I am aware of the problem :)
A first step could be to detect Alpine correctly, avoid downloading the binary and build from source
@aecz currently we download the binary for linux, the sanity check on the binary fails, and we fallback to compiling locally. The unnecessary download isn't ideal but it works and this flow is unlikely to change until we have an Alpine compatible binary.
Second step, download a prebuilt binary
This is what we're aiming for.
Unfortunately folk have jumped to the end of the issue to post unhelpful comments, rather than read the entire thread for the relevant information. As you can imagine, this is rather frustrating for us.
For those too lazy to read the entire thread the following two comments summarise where we are at, and what we need from y'all to move forward.
https://github.com/sass/node-sass/issues/1589#issuecomment-225760911 https://github.com/sass/node-sass/issues/1589#issuecomment-226849128
These are simple requests for those of you with Alpine environments. Sure we could do it ourselves but we're heavily preoccupied with current LibSass 3.4.0-RC1.
OK, well I can easily provide a process
dump....
FROM node:7.2.0-alpine
process {
title: 'node',
version: 'v7.2.0',
moduleLoadList:
[ 'Binding contextify',
'Binding natives',
'NativeModule events',
'Binding config',
'Binding icu',
'NativeModule util',
'Binding uv',
'NativeModule buffer',
'Binding buffer',
'Binding util',
'NativeModule internal/util',
'NativeModule internal/buffer',
'NativeModule timers',
'Binding timer_wrap',
'NativeModule internal/linkedlist',
'NativeModule assert',
'NativeModule internal/process',
'NativeModule internal/process/warning',
'NativeModule internal/process/next_tick',
'NativeModule internal/process/promises',
'NativeModule internal/process/stdio',
'Binding constants',
'NativeModule path',
'NativeModule module',
'NativeModule internal/module',
'NativeModule vm',
'NativeModule fs',
'Binding fs',
'NativeModule stream',
'NativeModule _stream_readable',
'NativeModule internal/streams/BufferList',
'NativeModule _stream_writable',
'NativeModule _stream_duplex',
'NativeModule _stream_transform',
'NativeModule _stream_passthrough',
'Binding fs_event_wrap',
'NativeModule internal/fs',
'NativeModule tty',
'NativeModule net',
'NativeModule internal/net',
'Binding cares_wrap',
'Binding tty_wrap',
'Binding tcp_wrap',
'Binding pipe_wrap',
'Binding stream_wrap',
'NativeModule console',
'NativeModule crypto',
'Binding crypto',
'NativeModule internal/streams/lazy_transform',
'NativeModule string_decoder',
'NativeModule os',
'Binding os',
'NativeModule url',
'NativeModule internal/url',
'Binding url',
'NativeModule querystring',
'NativeModule dns',
'NativeModule tls',
'NativeModule _tls_common',
'NativeModule _tls_wrap',
'NativeModule _stream_wrap',
'Binding js_stream',
'Binding tls_wrap',
'NativeModule _tls_legacy',
'NativeModule http',
'NativeModule _http_incoming',
'NativeModule _http_outgoing',
'NativeModule _http_common',
'Binding http_parser',
'NativeModule internal/freelist',
'NativeModule _http_agent',
'NativeModule _http_server',
'NativeModule _http_client',
'NativeModule zlib',
'Binding zlib' ],
versions:
{ http_parser: '2.7.0',
node: '7.2.0',
v8: '5.4.500.43',
uv: '1.10.1',
zlib: '1.2.8',
ares: '1.10.1-DEV',
modules: '51',
openssl: '1.0.2j',
icu: '58.1',
unicode: '9.0',
cldr: '30.0.2',
tz: '2016g' },
arch: 'x64',
platform: 'linux',
release:
{ name: 'node',
sourceUrl: 'https://nodejs.org/download/release/v7.2.0/node-v7.2.0.tar.gz',
headersUrl: 'https://nodejs.org/download/release/v7.2.0/node-v7.2.0-headers.tar.gz' },
argv:
[ '/usr/local/bin/node',
'/home/app/src/example/server/api/src' ],
execArgv: [ '--harmony' ],
env:
{ no_proxy: '*.local, 169.254/16',
npm_config_user_agent: 'yarn/0.18.0 npm/? node/v7.2.0 linux x64',
NODE_VERSION: '7.2.0',
HOSTNAME: 'e3e9b9f5cd3b',
npm_node_execpath: '/usr/local/bin/node',
npm_package_devDependencies_nodemon: '1.11.0',
npm_config_init_version: '1.0.0',
SHLVL: '4',
HOME: '/root',
npm_config_init_license: 'MIT',
npm_config_version_tag_prefix: 'v',
npm_package_dependencies_postgraphql: '2.3.0',
npm_package_dependencies_express: '4.14.0',
'npm_package_scripts_start:development': 'yarn start',
npm_package_private: 'true',
npm_package_scripts_lint: 'node_modules/eslint/bin/eslint.js src/** --color',
npm_config_registry: 'https://registry.yarnpkg.com',
npm_package_scripts_start: 'node --harmony src',
npm_config_ignore_scripts: '',
npm_package_dependencies_multer: '1.2.0',
npm_package_name: '@example/server-api',
PATH: '/home/app/src/example/server/api/node_modules/.bin:/root/.config/yarn/link/node_modules/.bin:/home/app/src/example/server/api/node_modules/.bin:/root/.config/yarn/link/node_modules/.bin:/usr/local/lib/node_modules/yarn/bin/node-gyp-bin:/home/app/src/example/server/api/node_modules/.bin:/root/.config/yarn/link/node_modules/.bin:/home/app/src/example/server/api/node_modules/.bin:/root/.config/yarn/link/node_modules/.bin:/usr/local/lib/node_modules/yarn/bin/node-gyp-bin:/home/app/src/example/server/api/node_modules/.bin:/root/.config/yarn/link/node_modules/.bin:/home/app/src/example/server/api/node_modules/.bin:/root/.config/yarn/link/node_modules/.bin:/usr/local/lib/node_modules/yarn/bin/node-gyp-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
NPM_CONFIG_LOGLEVEL: 'info',
'npm_package_scripts_start:development:watch': 'nodemon --config config/nodemon/nodemon.json',
npm_config_loglevel: '',
npm_config_version_git_message: 'v%s',
npm_lifecycle_event: 'start',
npm_package_version: '5.0.0',
npm_config_argv: '{"remain":[],"cooked":["start:development:watch"],"original":["start:development:watch"]}',
npm_package_dependencies_cors: '2.8.1',
npm_config_version_git_tag: 'true',
npm_config_version_git_sign: '',
'npm_package_scripts_prestart:development': 'yarn lint',
npm_config_strict_ssl: 'true',
'npm_package_devDependencies_@example/eslint-config-core-lint': 'file:../../core/eslint-config-lint',
npm_execpath: '/usr/local/lib/node_modules/yarn/bin/yarn.js',
PWD: '/home/app/src/example/server/api',
npm_config_save_prefix: '^',
npm_config_ignore_optional: '',
'npm_package_dependencies_@example/server-core': 'file:../core' },
pid: 124,
features:
{ debug: false,
uv: true,
ipv6: true,
tls_npn: true,
tls_alpn: true,
tls_sni: true,
tls_ocsp: true,
tls: true },
_needImmediateCallback: false,
execPath: '/usr/local/bin/node',
debugPort: 5858,
_startProfilerIdleNotifier: [Function: _startProfilerIdleNotifier],
_stopProfilerIdleNotifier: [Function: _stopProfilerIdleNotifier],
_getActiveRequests: [Function: _getActiveRequests],
_getActiveHandles: [Function: _getActiveHandles],
reallyExit: [Function: reallyExit],
abort: [Function: abort],
chdir: [Function: chdir],
cwd: [Function: cwd],
umask: [Function: umask],
getuid: [Function: getuid],
geteuid: [Function: geteuid],
setuid: [Function: setuid],
seteuid: [Function: seteuid],
setgid: [Function: setgid],
setegid: [Function: setegid],
getgid: [Function: getgid],
getegid: [Function: getegid],
getgroups: [Function: getgroups],
setgroups: [Function: setgroups],
initgroups: [Function: initgroups],
_kill: [Function: _kill],
_debugProcess: [Function: _debugProcess],
_debugPause: [Function: _debugPause],
_debugEnd: [Function: _debugEnd],
hrtime: [Function: hrtime],
cpuUsage: [Function: cpuUsage],
dlopen: [Function: dlopen],
uptime: [Function: uptime],
memoryUsage: [Function: memoryUsage],
binding: [Function: binding],
_linkedBinding: [Function: _linkedBinding],
_setupDomainUse: [Function: _setupDomainUse],
_events:
{ warning: [Function],
newListener: [Function],
removeListener: [Function] },
_rawDebug: [Function],
_eventsCount: 3,
domain: null,
_maxListeners: undefined,
_fatalException: [Function],
_exiting: false,
assert: [Function],
config:
{ target_defaults:
{ cflags: [],
default_configuration: 'Release',
defines: [],
include_dirs: [],
libraries: [] },
variables:
{ asan: 0,
coverage: false,
debug_devtools: 'node',
force_dynamic_crt: 0,
gas_version: '2.26',
host_arch: 'x64',
icu_data_file: 'icudt58l.dat',
icu_data_in: '../../deps/icu-small/source/data/in/icudt58l.dat',
icu_endianness: 'l',
icu_gyp_path: 'tools/icu/icu-generic.gyp',
icu_locales: 'en,root',
icu_path: 'deps/icu-small',
icu_small: true,
icu_ver_major: '58',
node_byteorder: 'little',
node_enable_d8: false,
node_enable_v8_vtunejit: false,
node_install_npm: true,
node_module_version: 51,
node_no_browser_globals: false,
node_prefix: '/usr/local',
node_release_urlbase: '',
node_shared: false,
node_shared_cares: false,
node_shared_http_parser: false,
node_shared_libuv: false,
node_shared_openssl: false,
node_shared_zlib: false,
node_tag: '',
node_use_bundled_v8: true,
node_use_dtrace: false,
node_use_etw: false,
node_use_lttng: false,
node_use_openssl: true,
node_use_perfctr: false,
node_use_v8_platform: true,
openssl_fips: '',
openssl_no_asm: 0,
shlib_suffix: 'so.51',
target_arch: 'x64',
uv_parent_path: '/deps/uv/',
uv_use_dtrace: false,
v8_enable_gdbjit: 0,
v8_enable_i18n_support: 1,
v8_inspector: true,
v8_no_strict_aliasing: 1,
v8_optimized_debug: 0,
v8_random_seed: 0,
v8_use_snapshot: true,
want_separate_host_toolset: 0,
want_separate_host_toolset_mkpeephole: 0 } },
emitWarning: [Function],
nextTick: [Function: nextTick],
_tickCallback: [Function: _tickCallback],
_tickDomainCallback: [Function: _tickDomainCallback],
stdout: [Getter],
stderr: [Getter],
stdin: [Getter],
openStdin: [Function],
exit: [Function],
kill: [Function],
argv0: 'node',
mainModule:
Module {
id: '.',
exports: {},
parent: null,
filename: '/home/app/src/example/server/api/src/index.js',
loaded: true,
children: [ [Object], [Object], [Object], [Object], [Object] ],
paths:
[ '/home/app/src/example/server/api/src/node_modules',
'/home/app/src/example/server/api/node_modules',
'/home/app/src/example/server/node_modules',
'/home/app/src/example/node_modules',
'/home/app/src/node_modules',
'/home/app/node_modules',
'/home/node_modules',
'/node_modules' ] },
_immediateCallback: [Function: processImmediate] }
@philostler thank you for this. For those playing along at home, as you can see from this dump
arch: 'x64',
platform: 'linux',
This is why our install process currently downloads the incompatible linux binary. What we need to proceed is a way to detect the currently environment as Alpine or Arch linux, that fails gracefully on non-*nix environments i.e Windows
$ docker run --rm node:7.2.0-alpine cat /etc/issue
Welcome to Alpine Linux 3.4
Kernel \r on an \m (\l)
That is not really workable... although this may be a rudimentary checkpoint '/etc/alpine-release'
@xzyfer any pointers on where would that detection happen? Presumably you'd want to detect a musl environment vs the distro, right?
If you were looking for a more robust way to detect musl, I'd probably just run ldd on /usr/local/bin/node:
ldd /usr/local/bin/node
/lib/ld-musl-x86_64.so.1 (0x562fdcd9a000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7efce03d7000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7efce01c4000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x562fdcd9a000)
specifically something like:
ldd $(which node) | grep -q libc.musl
That obviously wouldn't run on windows, but if ldd isn't available, it's safe to assume it's not a musl environment anyway.
Ignore the above @xzyfer, re-read thread and found Dockerfile and current status. I'll work on a Dockerfile for building alpine compatible versions based on that.
Seems like the approach @amrocha was using with different docker containers is the most sensible. That can easily be extended for iojs too and will be the most docker-cache efficient. Why the "ouch" @xzyfer (https://github.com/sass/node-sass/issues/1589#issuecomment-231901992)? Where does this actual build process happen so that I can land a PR?
What
We do not currently support Alpine Linux. It is commonly used to as a minimal base image with docker. Given the rising popularity of docker (and containerisation in general) I believe supporting Alpine Linux with a pre-built binaries is the right thing to do for the community.
Why
There is not explicit reason for us not supporting Alpine Linux, nor did make a conscious decision not to. We prebuild a "Linux" binaries which is targeted at popular distributions, namely those derived from Debian with a special consideration for CentOS 5. However that Linux is not compatible with the versions of libc(?) that ship with Alpine.
How
To achieve this we need to two things from the community.
Firstly we need a
Dockerfile
that when run for a given node-sass commit-ish, compiles the node-sass binary for all 32bit and 64bit version of Node 0.10, 0.12. iojs 1, iojs 2, iojs 3, Node 4, Node 5, Node 6. For reference here is the one we use for the Linux binariesSecondly a JavaScript function that reliable detects the current OS is Alpine Linux.