Dockerizing Node.js app - what does: ENV PATH /app/node_modules/.bin:$PATH - node.js

I went through one of very few good dockerizing Vue.js tutorials and there is one thing I don't understand why is mandatory in Dockerfile:
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /usr/src/app/package.json #not sure though how it relates to PATH...
I found only one explanation here which says:
We expose all Node.js binaries to our PATH environment variable and
copy our projects package.json to the app directory. Copying the JSON
file rather than the whole working directory allows us to take
advantage of Docker’s cache layers.
Still, it doesn't made me any smarter. Anyone able to explain it in plain english?

Error prevention
I think this is just a simple method of preventing an error where Docker wasn't able to find the correct executables (or any executables at all). Besides adding another layer to your image, there is in general as far as I know no downside in adding that line to your Dockerfile.
How does it work?
Adding node_modules/bin to the PATH environment variable ensures that the executables created during the npm build or the yarn build processes can be found. You could also COPY your locally builded node_modules folder to the image but it's advised to build it inside the Docker container to ensure all binaries are adapted to the underlying OS running in the container. The best practice would be to use multistage builds.
Furthermore, adding the node_modules/bin at the beginning of the PATH environment variable ensures that exactly these executables (from the node_modules folder) are used instead of any other executables which might also be installed on the system inside the Docker image.
Do I need it?
Short answer: Usually no. It should be optional.
Long answer: It should be enough to set the WORKDIR to the path where the node_modules is located for the issued RUN, CMD or ENTRYPOINT commands in your Dockerfile to find the correct binaries and therefore to successfully get executed. But I for example had a case where Docker wasn't able to find the files (I had a pretty complex setup with a so called devcontainer in VSCode). Adding the line ENV PATH /app/node_modules/.bin:$PATH solved my problem.
So, if you want to increase the stability of your Docker setup in order to make sure that everything works as expected, just add the line.

So I think the benefit of this line is to add the node_modules path from the Docker container to the list of PATHs on the relevant container. If you're on a Mac (or Linux I think) and run:
$ echo $PATH
You should see a list of paths which are used to run global commands from your terminal i.e. gulp, husky, yarn and so on.
The above command will add node_modules path to the list of PATHs in your docker container so that such commands if needed can be run globally inside the container they will work.

.bin (short for 'binaries') is a hidden directory, the period before the bin indicates that it is hidden. This directory contains executable files of your app's modules.
PATH is just a collection of directories/folders that contains executable files.
When you try to do something that requires a specific executable file, the shell looks for it in the collection of directories in PATH.
ENV PATH /app/node_modules/.bin:$PATH adds the .bin directory to this collection, so that when node tries to do something that requires a specific module's executable, it will look for it in the .bin folder.

For each command, like FROM, COPY, RUN, CMD, ..., Docker creates a image with the result of this command, and this images are called as layers. The final image is the result of merge of all layers.
If you use the COPY command to store all the code in one layer, it will be greater than store a environment variable with path of the code.
That's why the cache layers is a benefit.
For more info about layers, take a look at this very good article.

Related

File not found in Alpine Container, despite existing

I have an Alpine container that I copy a binary to (in this case it is spar). The entry point is dumb-init /usr/bin/spar but it results in a No such file or directory. When I run sh inside of the container, /usr/bin/spar exists. Trying to run it in
dumb-init ...
/usr/bin/spar / spar from /
./spar / spar from usr/bin/
All result in the same error. I tried changing the permissions with chmod 777 /usr/bin/spar giving everyone full access, still no luck.
Am I missing something that is specific to alpine? In another SO issue someone mentioned that switching from Alpine to Ubuntu solved their issue, but no further info was provided.
Here is the dockerfile that creates the image(s)
ARG intermediate=quay.io/wire/alpine-intermediate
ARG deps=quay.io/wire/alpine-deps
#--- Intermediate stage ---
FROM ${intermediate} as intermediate
#--- Minified stage ---
FROM ${deps}
ARG executable
ENV CACHEDEXECUTABLE ${executable}
COPY --from=intermediate /dist/${executable} /usr/bin/${executable}
# TODO: only if executable=brig, also copy templates. Docker image conditionals seem hacky:
# https://stackoverflow.com/questions/31528384/conditional-copy-add-in-dockerfile
# For now, adds ~2 MB of additional files into every container
COPY --from=intermediate /dist/templates/ /usr/share/wire/templates/
# ARGs are not available at runtime, create symlink at build time
# more info: https://stackoverflow.com/questions/40902445/using-variable-interpolation-in-string-in-docker
RUN ln -s /usr/bin/${executable} /usr/bin/service
ENTRYPOINT /usr/bin/dumb-init /usr/bin/${CACHEDEXECUTABLE}
If spar is a binary, that binary exists in the container, and you call it directly or it's in the containers path, then the two likely reasons for a file not found are:
Dynamically linked libraries that don't exist inside the container. E.g. of you run ldd spar it will show you those links, and there's a good chance you'll see libc despite trying to run on Alpine where it uses musl.
The binary is for another platform/architecture, and you have binfmt_misc setup, but without the --fix-binary option so it's looking for the interpreter path in the container filesystem rather than the host. This will be visible by the lack of an F flag in the /proc file for that platform.

mlflow run git-uri clone to specific directory

I am using mlflow run with a GitHub uri.
When I run using the below command
mlflow run <git-uri>
The command sets up a conda environment and then clones the Git repo into a temp directory, But I need it setup in a specific directory
I checked the entire document, but I can't find it. Is there no such option to do so in one shot?
For non-local URIs, MLflow uses the Python's tempfile.mkdtemp function (source code), that creates the temporary directory. You may have some control over it by setting the TMPDIR environment variable as described in Python docs (it lists TMP & TEMP as well, but they didn't work for me on MacOS) - but it will set only "base path" for temporary directories and files, the directory/file names are still will be random.

How to run a dockerfile?

Found a dockerfile that want to create image and run:
https://gist.github.com/matsuu/d5b4e83b3d591441f01b7be2ede774e2
Stored it in a new folder as centos-redhat-8-beta.dockerfile on my computer and tried:
docker build -t centos-redhat-8-beta .
unable to prepare context: unable to evaluate symlinks in Dockerfile path:
lstat /Users/dnk306/docker/centos-redhat-8-beta/Dockerfile: no such file or directory
What is exact command that need to run?
Dockerfile is not an extension, per default the file should be called Dockerfile for the build command to use it.
If you want to use a different name, though, the option -f or flag --file can help you achieve this.
docker build -t centos-redhat-8-beta -f centos-redhat-8-beta.dockerfile .
From the documentaion:
By default the docker build command will look for a Dockerfile at the root of the build context. The -f, --file, option lets you specify the path to an alternative file to use instead. This is useful in cases where the same set of files are used for multiple builds. The path must be to a file within the build context. If a relative path is specified then it is interpreted as relative to the root of the context.
Source: https://docs.docker.com/engine/reference/commandline/build/#text-files

Where is the gulp cli executable and where does it run from?

I'm normally in java-land but for a few weeks I've received a wordpress site that uses gulp and I need to know some npm/nodejs to understand it.
I want to define a builder in eclipse that will build the output files from gulp automatically after each time I save my resource files (js/css). This requires me to point to an executable CLI program.
The thing I can't figure out is where do all these packages' CLIs get stored? It seems my local project has a node_modules directory AND my /usr/local/lib has a node_modules directory and I'm assuming one is for local plugins and the other is for global ones.
My question is when I saw "gulp" in a CLI in my project root directory, how does it know it should run an rpm plugin cli program and where does it find it?
See https://docs.npmjs.com/files/folders:
When in global mode, executables are linked into {prefix}/bin on Unix, or directly into {prefix} on Windows.
When in local mode, executables are linked into ./node_modules/.bin so that they can be made available to scripts run through npm. (For example, so that a test runner will be in the path when you run npm test.)
The {prefix} config defaults to the location where node is installed. On most systems, this is /usr/local. On windows, this is the exact location of the node.exe binary. On Unix systems, it's one level up, since node is typically installed at {prefix}/bin/node rather than {prefix}/node.exe.

Running a program from the source tree

Should it generally be possible to run a program from the source directory (src) after having invoked ./configure and make (but not make install)? I'm trying to fix a bug in an application and it seems unnecessary to run make install after each code change. Unfortunately I can't run the application in the source directory since it tries to access files in the lib installation directory (which do not exist before make install). Is the application wrongly configured or do I have to reinstall it after each change to the source code?
It all depends on the application and what components or files it expects to be visible and where. But assuming no required configuration or dependencies, then yes, you can run the program in-place.
To add a directory to your lib search path, add to the environment variable LD_LIBRARY_PATH. Like so:
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/home/user/myproject/lib" ./someprogram
Note that specifiying a variable assignment on the command line in front of the program you run sets that variable for that run only. (Note, no semicolon -- this is a single command.) If you want to set the variable for the entire session, use
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/home/user/myproject/lib"
I'd recommend against this, though. It can lead to problems and confusion.

Resources