1

I have already created a docker-compose.yaml file, Dockerfile and entrypoint.sh file. Dockerfile was used to create a ubuntu container. Inside this ubuntu container, I install Node.js and clone my project repository, then run the project. The project can be run without any problem. But, need to execute below function written in run.js file.

const users = require("./user");
users();

the console.log() is inside the user.js file,

function users() {
    console.log("I am the function in user.js")
}

module.exports = users;

docker-compose.yaml :,

version: '3'
services:
  node-backend:
    build:
      context: .
      dockerfile: Dockerfile
    image: cron-api
    container_name: cron-api-cont
    ports:
      - 3002:3002
    entrypoint: /app/entrypoint.sh

Dockerfile :

FROM ubuntu:22.04

# Install necessary dependencies
RUN apt-get update && \
    apt-get install -y git curl cron

# Install Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
RUN apt-get install -y nodejs

# Create app working directory
WORKDIR /app

# Copy the entrypoint script into the container
COPY entrypoint.sh /app/entrypoint.sh


# Set executable permissions for the entrypoint script
RUN chmod +x /app/entrypoint.sh


# Expose the app port
EXPOSE 3002

entrypoint.sh :

!/bin/bash
# Set GitHub credentials
GIT_USERNAME="username"
GIT_TOKEN="my_token_private"
BRANCH="main"  


# Clone the repository using credentials
git clone -b $BRANCH "https://${GIT_USERNAME}:${GIT_TOKEN}@github.com/username/node-for-docker-test.git" /app/api


# Check if the cloning process is complete
if [ -d "/app/api" ]; then
  echo "Cloning completed successfully."
  echo "List of directories inside api:"
  ls -d /app/api
else
  echo "Cloning failed."
fi

# Go to the backend directory
cd  /app/api

# Install node modules
npm ci

# Start the server
npm start &


# Schedule the cron job to execute the users() function every 5 minutes
echo "*/1 * * * * node /app/api/run.js" > /tmp/cron_job.txt
echo "Cron job definition created."

# Check if the run.js file exists and print its content
run_js_file="/app/api/run.js"
if [ -f "$run_js_file" ]; then
    echo "Found run.js file. Content:"
    cat "$run_js_file"
else
    echo "run.js file not found."
fi

# Install the cron job from the file
crontab /tmp/cron_job.txt
echo "Cron job installed."

# Start the cron service
service cron start &
echo "Cron service started."

# Keep the container running
tail -f /dev/null

Using the entrypoint.sh, I clone the repo and install node modules. then start the project. I do not want to write another .sh for the cron job. When I write the command inside the /app/ or /app/api/ directory inside container, it runs the users() function when I execute node /app/api/run.js manually.

root@6ef26d9188:/app/api# ls
index.js  node_modules  package-lock.json  package.json  run.js  user.js
root@6ef26d9188:/app/api# pwd
/app/api
root@6ef26d9188:/app/api# node /app/api/run.js
I am the function in user.js

Inside the docker, the cron service runs .After run the container, output of crontab -l is;

root@6ef26d9188:/# crontab -l
*/1 * * * * node /app/api/run.js

output of service cron status is;

root@6ef26d918826:/# service cron status
 * cron is running

This is the log of the container:

 ✔ Container cron-api-cont  Created                                        0.1s 
Attaching to cron-api-cont
cron-api-cont  | Cloning into '/app/api'...
cron-api-cont  | Cloning completed successfully.
cron-api-cont  | List of directories inside api:
cron-api-cont  | /app/api
cron-api-cont  | 
cron-api-cont  | added 90 packages, and audited 91 packages in 3s
cron-api-cont  | 
cron-api-cont  | 11 packages are looking for funding
cron-api-cont  |   run `npm fund` for details
cron-api-cont  | 
cron-api-cont  | 3 moderate severity vulnerabilities
cron-api-cont  | 
cron-api-cont  | To address all issues (including breaking changes), run:
cron-api-cont  |   npm audit fix --force
cron-api-cont  | 
cron-api-cont  | Run `npm audit` for details.
cron-api-cont  | npm notice 
cron-api-cont  | npm notice New major version of npm available! 8.19.4 -> 9.7.2
cron-api-cont  | npm notice Changelog: <https://github.com/npm/cli/releases/tag/v9.7.2>
cron-api-cont  | npm notice Run `npm install -g npm@9.7.2` to update!
cron-api-cont  | npm notice 
cron-api-cont  | Cron job definition created.
cron-api-cont  | Found run.js file. Content:
cron-api-cont  | const users = require("./user");
cron-api-cont  | 
cron-api-cont  | users();Cron job installed.
cron-api-cont  | Cron service started.
cron-api-cont  |  * Starting periodic command scheduler cron
cron-api-cont  |    ...done.
cron-api-cont  | 
cron-api-cont  | > cron_tutorial@1.0.0 start
cron-api-cont  | > nodemon index.js
cron-api-cont  | 
cron-api-cont  | [nodemon] 2.0.22
cron-api-cont  | [nodemon] to restart at any time, enter `rs`
cron-api-cont  | [nodemon] watching path(s): *.*
cron-api-cont  | [nodemon] watching extensions: js,mjs,json
cron-api-cont  | [nodemon] starting `node index.js`
cron-api-cont  | server is running on port 3002

Can anyone tell, why it does not print the console log result on the terminal for every 1 minute?

Ke_Sandaru
  • 99
  • 9

3 Answers3

3

Normally a Docker container only runs a single process. If you need multiple processes, you need to run multiple containers. This applies to the cron daemon too: since it's a separate process, it needs its own container. This should be simple to set up in the docker-compose.yml:

version: '3.8'
services:
  node-backend:
    build: .
    ports:
      - 3002:3002
  cron-run:
    build: .
    command: cron -n

(This is the entire Compose file; you do not need image: for things you build:, and you do not normally need to set container_name:, networks:, or to manually override the entrypoint:.)

For this to work well, I'd suggest building your application only once in the Dockerfile, rather than repeating it every single time you start the container. Most of what's in the entrypoint script should move into the Dockerfile. Don't run git inside Docker at all; instead, add the docker-compose.yml and Dockerfile to your application's source repository, and have the Dockerfile build whatever's currently checked out.

FROM node:16-slim  # based on Debian

# Get the cron daemon
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --no-install-recommends --assume-yes \
      cron

# Install the application (boilerplate Node app setup)
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY ./ ./
# RUN npm build

# Set up the cron job
RUN echo '*/1 * * * * node /app/api/run.js' | crontab

# By default, run the application
CMD npm start

Note that there is only one process per container now. We never launch a background process and don't try to "start services"; this concept doesn't really exist in a container and the service command does not work reliably. We especially do not need to "keep the container alive". One container is the Node application and one is the cron daemon, and the two containers will stay alive on their own so long as their respective processes are running.

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • 1
    Thanks for this suggestion.! Actually, still my project is in development phase. The purpose of using this kind of entrypoint.sh was to pull the latest code from GitHub. It was easy other than using CI/CD due to less resources. The code updates frequently, so I thought this is a good option to use an Ubuntu image and put latest code inside it. It was easy to run my latest code in server. Is it a bad practice? However do I keep two containers for node app and Cron ? – Ke_Sandaru Jul 04 '23 at 11:21
  • 2
    For that sort of use case, I'd use Git and Node directly on your host system; I wouldn't involve Docker at all. Note that this setup runs two _containers_ off the same _image_; when you deploy this to the server, both containers can have the same `image:` line pointing at a copy of the image you've uploaded to a registry, and neither needs to `build:` (you should not directly need the source code on the remove server). – David Maze Jul 04 '23 at 11:27
  • I will improve the project into this level.great! – Ke_Sandaru Jul 07 '23 at 03:49
1

In your entrypoint.sh, you have this line:

echo "*/1 * * * * node /app/api/run.js" > /tmp/cron_job.txt

The ">" character would reroute the output of the command to the /tmp/cron_job.txt, so the output does not appear on your console. Therefore, it is also not logged to your container log. Notice how you do not use the ">" character on any other echo command, which all show up in the log as you expect.

According to this answer, you want to use the tee command, e.g.

echo "*/1 * * * * node /app/api/run.js" | tee /tmp/cron_job.txt

Check whether the output is in the /tmp/cron_job.txt file and see if you can log the output of the cronjob to both the console and the file.

Also, small nitpick: Your comment in entrypoint.sh says to run the cronjob every five minutes, when you actually configure it to run every minute.

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
CloudWatcher
  • 181
  • 1
  • 11
  • thanks for your answer. Here I changed the entrypoint.sh and tested. But, still it does not print the logs on console. cron-api-cont | [nodemon] starting `node index.js` cron-api-cont | server is running on port 3002 – Ke_Sandaru Jul 04 '23 at 10:23
  • Do you only need the information that the job ran? In that case, you can problebly do something like this: `echo "*/1 * * * * echo "Job ran!"; node /app/api/run.js"` following [this tutorial](https://www.sitepoint.com/cron-jobs/#:~:text=Multiple%20Commands%20in%20the%20Same,semi-colon%20(%20%3B%20).&text=If%20the%20running%20commands%20depend,if%20the%20first%20one%20fails.) on multiple commands. You will have to play around with escaping the quotes here. – CloudWatcher Jul 04 '23 at 10:35
  • If this job runs, it should write this "I am the function in user.js" on the console. But it does not print it. So I can not know the cron job works for every 1 minute. – Ke_Sandaru Jul 04 '23 at 10:40
  • I will try this. Thnks! – Ke_Sandaru Jul 04 '23 at 10:42
  • So there is a logging command in user.js? That was previously unclear. Please also show that code. – CloudWatcher Jul 04 '23 at 10:43
  • sorry, I updated the code. I have shown that function now. logging means a console.log(), sorry for that – Ke_Sandaru Jul 04 '23 at 11:14
  • If anyone reads, this is how I solved the problem. On Node.js API terminal, I executed the project and then it shows successful message. But, after that nothing can be seen on terminal either cronjob runs. I used console.log() in a cronjob. – Ke_Sandaru Jul 07 '23 at 03:44
  • 1
    Furthermore, I should write the cronjob into like "echo "*/2 * * * * cd /app/api && /usr/bin/node -e \"require('/app/api/run.js').users()\" >> /var/logfile_new.log 2>&1" | tee /tmp/cron_job.txt". Then I could open 'logfile_new.log' and see the logs. Every time the cronjob runs, it should be directed to /app/api directory (where node modules were installed), otherwise it will not be executed (could not find module error). – Ke_Sandaru Jul 07 '23 at 03:44
  • Great that you solved your problem! If your solution differs significantly from the answers given here, you could add it as an answer in itself and accept it as the solution. That would be more clear for people with similar problems. – CloudWatcher Jul 07 '23 at 05:43
0

This is how I get solved the problem. In Node.js API terminal, I executed the project and then it shows successful message. I have written console.log() in a cronjob. But, after the project executed nothing can be seen on terminal relevant to the running cronjob.

Then as my solution, I included the cronjob into entrypoint.sh file as echo "*/2 * * * * cd /app/api && /usr/bin/node -e \"require('/app/api/run.js').users()\" >> /var/logfile_new.log 2>&1" | tee /tmp/cron_job.txt. Then I could open logfile_new.log and see the logs. Every time the cronjob runs, it should be directed to /app/api directory (to where node modules were installed), otherwise the function written in the cronjob will be executed without finding the run.js module and writes could not find module error into the logfile_new.log.

This answer will be useful, when anyone tries to execute a single function in a node.js module as a cronjob.

Ke_Sandaru
  • 99
  • 9