Cannot run nodejs app and mongo within a docker container - node.js

I'm setting up a container with the following Dockerfile
# Start with project/baseline
FROM project/baseline # => image with mongo / nodejs / sailsjs
# Create folder that will contain all the sources
RUN mkdir -p /var/project
# Load the configuration file and the deployment script
ADD init.sh /var/project/init.sh
ADD src/ /var/project/ # src contains a list of folder, each one being a sails app
# Compile the sources / run the services / run mongodb
CMD /var/project/init.sh
The init.sh script is called when the container runs.
It should start a couple of webapp and mongodb.
#!/bin/bash
PROJECT_PATH=/var/project
# Start mongodb
function start_mongo {
mongod --fork --logpath /var/log/mongodb.log # attempt to have mongo running in daemon
}
# Start services
function start {
for service in $(ls);do
cd $PROJECT_PATH/$service
npm start # Runs sails lift on each service
done
}
# start mongodb
start_mongo
# start web applications defined in /var/project
start
Basically, there is a couple of nodejs (sailsjs) application in /var/project.
When I run the container, I got the following message:
$ sudo docker run -t -i projects/test
about to fork child process, waiting until server is ready for connections.
forked process: 10
and then it remains stuck.
How can mongo and the sails processes can be started and the container to remain in a running state ?
UPDATE
I now use this supervisord.conf file
[supervisord]
nodaemon=false
[program:mongodb]
command=/usr/bin/mongod
[program:process1]
command=/bin/bash "cd /var/project/service1 && node app.js"
[program:process2]
command=/bin/bash "cd /var/project/service2 && node app.js"
it is called in the Dockerfile like:
# run the applications (mongodb + project related services)
CMD ["/usr/bin/supervisord"]
As my services are dependent upon mongo starting correctly, supervisord does not wait that long and the services are not started then. Any idea to solve that ?
By the way, it that a so best practice to use mongo in the same container ?
UPDATE 2
I went back to a service.sh script that is called when the container is running. I know this is not clean (but I'll say it's temporary so I can fix the pb I have in supervisor), but I'm doing the following:
run nohup mongod &
wait 60 sec
run my node (forever) processes
The thing is, the container exit right after the forever processes are ran... how can it be kept active ?

If you want to cleanly start multiple services inside a container, one option is to use a process supervisor of some sort. One option is documented here, in the official Docker documentation.
I've done something similar using runit. You can see my base runit image here, and a multi-service application image using that here.

Related

How to auto start node server after creating VS Code Development Container?

I am using VS Code's feature to create development containers for my services. Using the following layout, I've defined a single service (for now). I'd like to automatically run my node project after the container is configured to listen for http requests but haven't found the best way to do so.
My Project Directory
project-name
.devcontainer.json
package.json (etc)
docker-compose.yaml
Now in my docker-compose.yaml, I've defined the following structure:
version: '3'
services:
project-name:
image: node:14
command: /bin/sh -c "while sleep 1000; do :; done"
ports:
- 4001:4001
restart: always
volumes:
- ./:/workspace:cached
Note how I need to have /bin/sh -c "while sleep 1000; do :; done" as the service command, which is required according to VS Code docs so that the service doesn't close?
Within my .devcontainer.json:
{
"name": "My Project",
"dockerComposeFile": [
"../docker-compose.yaml"
],
"service": "project-name",
"shutdownAction": "none",
"postCreateCommand": "npm install",
"postStartCommand": "npm run dev" // this causes the project to hang while configuring?
"workspaceFolder": "/workspace/project-name"
}
I've added a postCreateCommand to install dependencies, but I also need to run npm run dev to have my server listen for requests. However, if I add this command in the postStartCommand, the project does build and run, but it technically hangs on Configuring Dev Server (with a spinner at the bottom of VS Code) since this starts my server and the script doesn't "exit", so I feel like there should be a better way to trigger the server to run after the container is set up?
See https://code.visualstudio.com/remote/advancedcontainers/start-processes
In other cases, you may want to start up a process and leave it running. This can be accomplished by using nohup and putting the process into the background using &. For example:
"postStartCommand": "nohup bash -c 'your-command-here &'"
I just tried it, and it works for me - it removes the spinning "Configuring Dev Container" that I also saw. However, it does mean the process is running in the background so your logs will not be surfaced to the devcontainer terminal. I got used to watching my ng serve logs in that terminal to know when compilation was done, but now I can't see them. Undecided if I'll switch back to the old way. Having the Configuring Dev Container spinning constantly was annoying but otherwise did not obstruct anything that I could see.

Kubernetes fails to deploy valid container image

I have a docker image containing an NodeJS app. The Dockerfile is:
FROM node:8
WORKDIR /app
ADD . /app
RUN npm install
EXPOSE 80
ENTRYPOINT [ "/bin/sh", "./start.sh" ]
The start.sh script is:
#!/bin/bash
...
echo "Starting application"
npm start
I'm able to launch and test the image manually:
$ gcloud docker -- run -it --rm my-container
...
Starting application
...
> node index.js
...
The same container is used by a kubernetes deployment:
apiVersion: extensions/v1beta1
kind: Deployment
...
spec:
...
template:
...
spec:
containers:
- image: my-container
...
The container starts, the start.sh script is correctly executed but it terminates and the container goes into a CrashLoopBackOff loop.
After inspecting the pod manually:
kubectl exec -ti my-pod -- bash
I have no name!#my-pod:/app# cat /etc/passwd
... empty response
-> It appears that somehow there are no system users on the container, which makes most commands (like npm) fail silently and terminate the container
I have also tried, without success:
deleting the pod
deleting and re-creating the deployment
running the node image with the node user -> unable to find user node: no matching entries in passwd file
Last note: I actually have many deployments (using the same template with just a different name) which are running fine with an image that was built a few days ago with the same source code.
For some deployments, it actually worked after manually deleting the pod and letting kubernetes recreate it.
Any ideas?
Edit 18/01/2018 I have tried rebuilding an image with the same source code that old working images use, without success. I have also tried a simpler Dockerfile:
FROM node:8
USER node
But I still get an error related to the fact that no users seem to be there:
Error response from daemon: {"message":"linux spec user: unable to find user node: no matching entries in passwd file"}
I have checked with the docker-node guys, the image hasn't changed recently. Could it be related to kubernetes changes? Keep it mind that my images do run when I run them manually with the docker command.
I tried to reproduce your issue, but didn't get it to fail in anything like the same fashion. I made a dummy express app and stuck it on github that matches your example above, and then invoked it into a local minikube instance I had. The base image size is reasonably large, but it started up just fine.
I had to interpret what was happening within npm start for your example since you didn't specify, but you can see my package.json, which I suspect is pretty close to what you're doing based on the description.
When I fire this up:
git clone https://github.com/heckj/dummyexpress
cd dummyexpress
kubectl apply -f deploy/
The I got a running instance right off the bat:
NAME READY STATUS RESTARTS AGE
dummynodeapp-7788b95497-tkw2s 1/1 Running 0 1d
And the logs show pretty much what you'd expect:
**kubectl log dummynodeapp-7788b95497-tkw2s**
W0117 19:41:00.986498 20648 cmd.go:353] log is DEPRECATED and will be removed in a future version. Use logs instead.
Starting application
> blah#1.0.0 start /app
> node index.js
Example app listening on port 3000!
My guess is that you've got something going awry within your npm start execution, so I'd recommend fiddling with that aspect of your deployment and see if you can't resolve it that way.
Well as #heckj pointed out, it was a Docker issue on my kubernetes cluster. I updated the cluster from 1.6.13-gke.1 to v1.7.12-gke.0 and the pods worked fine again. I'm not sure what Docker version was used since there's another kubernetes bug that is preventing me from seeing it.

Bash script fails to run node on docker image

I have an app that I want to run on a single self contained Docker image.
I had it running fine on an Ubuntu based image, but the same script now causes me trouble on Alpine.
Here is my docker file :
FROM julienlengrand/alpine-node-rethinkdb
# Preparing
# RUN ln -snf /bin/bash /bin/sh
# # Define mountable directories.
VOLUME ["/data"]
# # Define working directory.
WORKDIR /data
# # Install app dependencies
COPY package.json /data
RUN npm install
# # Bundle app source
COPY . /data
# # Expose rethinkdb ports.
# - 8080: web UI
# - 28015: process
# - 29015: cluster
EXPOSE 8080
#EXPOSE 28015
#EXPOSE 29015
# Expose node app ports
EXPOSE 4567
CMD [ "/bin/sh", "/data/startApp.sh" ]
My startApp script is relatively simple :
#!/bin/sh
rethinkdb --bind all & sleep 1; node dbCreate.js; sleep 2; nohup node workers/worker.js & node app.js
But when I try to run it, I get the following error:
module.js:442
throw err;
^
'rror: Cannot find module '/data/app.js
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.runMain (module.js:575:10)
at run (bootstrap_node.js:352:7)
at startup (bootstrap_node.js:144:9)
at bootstrap_node.js:467:3
This happens whether I run it automatically, or directly within the image using the shell.
I have checked and everything is correctly placed in the data folder.
Additionally, if I run all the commands one after the other directly in the sh shell everything runs as expected.
I have also tried to simplify my script as such :
#!/bin/sh
rethinkdb --bind all & sleep 1; node dbCreate.js; sleep 2; node app.js
bu the same issue happens.
Any idea what can go wrong? What could make my /data folder unavailable when running via the startApp script? Could it be that it is a specificity from Alpine?
Thanks,
The error message you are receiving looks like a classic carriage return issue as the quote after app.js has moved to the start of the line
'rror: Cannot find module '/data/app.js
Node should normally be able to deal with both line endings but shell scripts aren't so kind.
I generally default all projects/files/editors/git to a Unix \n unless there are specific requirements not too.
You can convert existing files with dos2unix or one of the answers in the question jlengrand found. I like perl -pi -e 's/\r\n/\n/g', because pie

docker stop doesn't work for node process

I want to be able to run node inside a docker container, and then be able to run docker stop <container>. This should stop the container on SIGTERM rather than timing out and doing a SIGKILL. Unfortunately, I seem to be missing something, and the information I have found seems to contradict other bits.
Here is a test Dockerfile:
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y curl
RUN curl -sSL http://nodejs.org/dist/v0.11.14/node-v0.11.14-linux-x64.tar.gz | tar -xzf -
ADD test.js /
ENTRYPOINT ["/node-v0.11.14-linux-x64/bin/node", "/test.js"]
Here is the test.js referred to in the Dockerfile:
var http = require('http');
var server = http.createServer(function (req, res) {
console.log('exiting');
process.exit(0);
}).listen(3333, function (err) {
console.log('pid is ' + process.pid)
});
I build it like so:
$ docker build -t test .
I run it like so:
$ docker run --name test -p 3333:3333 -d test
Then I run:
$ docker stop test
Whereupon the SIGTERM apparently doesn't work, causing it to timeout 10 seconds later and then die.
I've found that if I start the node task through sh -c then I can kill it with ^C from an interactive (-it) container, but I still can't get docker stop to work. This is contradictory to comments I've read saying sh doesn't pass on the signal, but might agree with other comments I've read saying that PID 1 doesn't get SIGTERM (since it's started via sh, it'll be PID 2).
The end goal is to be able to run docker start -a ... in an upstart job and be able to stop the service and it actually exits the container.
My way to do this is to catch SIGINT (interrupt signal) in my JavaScript.
process.on('SIGINT', () => {
console.info("Interrupted");
process.exit(0);
})
This should do the trick when you press Ctrl+C.
Ok, I figured out a workaround myself, which I'll venture as an answer in the hope it helps others. It doesn't completely answer why the signals weren't working before, but it does give me the behaviour I want.
Using baseimage-docker seems to solve the issue. Here's what I did to get this working with the minimal test example above:
Keep test.js as is.
Modify Dockerfile to look like the following:
FROM phusion/baseimage:0.9.15
# disable SSH
RUN rm -rf /etc/service/sshd /etc/my_init.d/00_regen_ssh_host_keys.sh
# install curl and node as before
RUN apt-get update && apt-get install -y curl
RUN curl -sSL http://nodejs.org/dist/v0.11.14/node-v0.11.14-linux-x64.tar.gz | tar -xzf -
# the baseimage init process
CMD ["/sbin/my_init"]
# create a directory for the runit script and add it
RUN mkdir /etc/service/app
ADD run.sh /etc/service/app/run
# install the application
ADD test.js /
baseimage-docker includes an init process (/sbin/my_init) which handles starting other processes and dealing with zombie processes. It uses runit for service supervision. The Dockerfile therefore sets the my_init process as the command to run on boot, and adds a script /etc/service for runit to pick it up.
The run.sh script is simple:
#!/bin/sh
exec /node-v0.11.14-linux-x64/bin/node /test.js
Don't forget to chmod +x run.sh!
By default, runit will automatically restart the service if it goes down.
Following these steps (and build, run, and stop as before), the container properly responds to requests for it to shutdown, in a timely fashion.

Nodejs/Strongloop: working upstart config example

After update strongloop to v2.10 slc stops writing logs.
Also I couldn't make the app to start in production mode.
/etc/init/app.conf
#!upstart
description "StrongLoop app"
start on startup
stop on shutdown
env NODE_ENV=production
script
exec slc run /home/ubuntu/app/ \
-l /home/ubuntu/app/app.log \
-p /var/run/app.pid
end script
Can anybody check my upstart config or provide another working copy?
Are you were writing the pid to a file so that you can use it to send SIGUSR2 to the process to trigger log re-opening from logrotate?
Assuming you are using Upstart 1.4+ (Ubuntu 12.04 or newer), then you would be better off letting slc run log to its stdout and let Upstart take care of writing it to a file so that log rotation is done for you:
#!upstart
description "StrongLoop app"
start on startup
stop on shutdown
# assuming this is /etc/init/app.conf,
# stdout+stderr logged to: /var/log/upstart/app.log
console log
env NODE_ENV=production
exec /usr/local/bin/slc run --cluster=CPUs /home/ubuntu/app
The log rotation for "free" is nice, but the biggest benefit to this approach is Upstart can log errors that slc run reports even if they are a crash while trying to set up its internal logging, which makes debugging a lot easier.
Aside from what it means to your actual application, the only effect NODE_ENV has on slc run is to set the default number of cluster workers to the number of detected CPU cores, which literally translates to --cluster=CPUs.
Another problem I find is the node/npm path prefix not being in the $PATH as used by Upstart, so I normally put the full paths for executables in my Upstart jobs.
Service Installer
You could also try using strong-service-install, which is a module used by slc pm-install to install strong-pm as an OS service:
$ npm install -g strong-service-install
$ sudo sl-svc-install --name app --user ubuntu --cwd /home/ubuntu/app -- slc run --cluster=CPUs .
Note the spaces around the -- before slc run

Resources