Search code examples
dockerloggingcrondocker-composetty

Docker ubuntu cron tail logs not visible


Trying to run a docker container that has a cron scheduling. However I cannot make it output logs.

Im using docker-compose.

docker-compose.yml

---
version: '3'
services:
  cron:
    build:
      context: cron/
    container_name: ubuntu-cron

cron/Dockerfile

FROM ubuntu:18.10

RUN apt-get update
RUN apt-get update && apt-get install -y cron 

ADD hello-cron /etc/cron.d/hello-cron

# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron

# Create the log file to be able to run tail
RUN touch /var/log/cron.log

# Run the command on container startup
CMD cron && tail -F /var/log/cron.log

cron/hello-cron

* * * * * root echo "Hello world" >> /var/log/cron.log 2>&1

The above runs fine its outputting logs inside the container however they are not streamed to the docker.

e.g. docker logs -f ubuntu-cron returns empty results

but

if you login to the container docker exec -it -i ubuntu-cron /bin/bash you have logs.

cat /var/log/cron.log 
Hello world
Hello world
Hello world

Now Im thinking that maybe I dont need to log to a file? could attach this to sttoud but not sure how to do this.

This looks similar... How to redirect cron job output to stdout


Solution

  • Due to some weirdness in the docker layers and inodes, you have to create the file during the CMD:

    CMD cron && touch /var/log/cron.log && tail -F /var/log/cron.log
    

    This works both for file and stdout:

    FROM ubuntu:18.10
    
    RUN apt-get update
    RUN apt-get update && apt-get install -y cron
    
    ADD hello-cron /etc/cron.d/hello-cron
    
    # Give execution rights on the cron job
    RUN chmod 0644 /etc/cron.d/hello-cron
    
    # Create the log file to be able to run tail
    # Run the command on container startup
    CMD cron && touch /var/log/cron.log && tail -F /var/log/cron.log
    

    The explanation seems to be this one:

    In the original post tail command starts "listening" to a file which is in a layer of the image, then when cron writes the first line to that file, docker copies the file to a new layer, the container layer (because of the nature of copy-and-write filesystem, the way that docker works). So when the file gets created in a new layer it gets a different inode and tail keeps listening in the previous state, so looses every update to the "new file". Credits BMitch