5

I want to include a cron task in a MariaDB container, based on the latest image mariadb, but I'm stuck with this.

I tried many things without success because I can't launch both MariaDB and Cron.

Here is my actual dockerfile:

FROM mariadb:10.3

# DB settings
ENV MYSQL_DATABASE=beurre \
    MYSQL_ROOT_PASSWORD=beurette

COPY ./data /docker-entrypoint-initdb.d
COPY ./keys/keys.enc home/mdb/
COPY ./config/encryption.cnf /etc/mysql/conf.d/encryption.cnf

# Installations
RUN apt-get update && apt-get -y install python cron

# Cron
RUN touch /etc/cron.d/bp-cron
RUN printf '* * * * * root echo "Hello world" >> /var/log/cron.log 2>&1\n#' >> /etc/cron.d/bp-cron
RUN touch /var/log/cron.log
RUN chmod 0644 /etc/cron.d/bp-cron

RUN cron

With its settings, the database starts correctly, but "Cron" is not initialized. To make it work, I have to get into the container and execute the "Cron" command, and everything works perfectly.

So I'm looking for a way to launch both the db and cron from my Dockerfile used in my docker-compose.

If this is not possible, maybe there is another way to do tasks planned? The purpose being to execute a script of the db.

k0pernikus
  • 60,309
  • 67
  • 216
  • 347
Kirian CAUMES
  • 133
  • 2
  • 9
  • Related: https://stackoverflow.com/questions/37458287/how-to-run-a-cron-job-inside-a-docker-container – k0pernikus Jun 28 '19 at 09:01
  • 2
    Sidenote: I would not necessarily blow-up the functionality of the mariadb container. Rather link it with another container that connects to the database that runs your scripts for better separation of concerns. – k0pernikus Jun 28 '19 at 09:05
  • I've already tried that, but the problem is that the cron is running in the foreground and Mariadb is no longer running. I would also like to avoid this second solution, but if it is the best then why not! Do you have a link I can refer to? Many thanks – Kirian CAUMES Jun 28 '19 at 09:17
  • Possible duplicate of [How to run a cron job inside a docker container?](https://stackoverflow.com/questions/37458287/how-to-run-a-cron-job-inside-a-docker-container) – Nico Haase Jun 28 '19 at 09:17
  • Well, then use a seperate container that does not interfere with the database container – Nico Haase Jun 28 '19 at 09:18

2 Answers2

6

Elaborating on @k0pernikus's comment, I would recommend to use a separate container that runs cron. The cronjobs in that container can then work with your mysql database.

Here's how I would approach it:

1. Create a Cron Docker Container

You can set up a cron container fairly simply. Here's an example Dockerfile that should do the job:

FROM alpine
COPY ./crontab /etc/crontab
RUN crontab /etc/crontab
RUN touch /var/log/cron.log
CMD crond -f

Just put your crontab into a crontab file next to that Dockerfile and you should have a working cron container.

An example crontab file:

* * * * * mysql -h mysql --execute "INSERT INTO database.table VALUES 'v';"

2. Add the cron container to your docker-compose.yml as a service

Make sure you add your cron container to the docker-compose.yml, and put it in the same network as your mysql service:

networks:
    my_network:
services:
    mysql:
        image: mariadb
        networks:
          - my_network
    cron:
        image: my_cron
        depends_on: 
          - mysql
        build:
            context: ./path/to/my/cron-docker-folder
        networks:
          - my_network
fjc
  • 5,590
  • 17
  • 36
  • All right, it works! Many thanks ! I just had to install mysql on the "cron" container. But now, I would like to run a "py" script, or at worst "sh", located on the Mariadb container from the Cron container. How can I do this? I'm trying with SSH access, but without success for the moment.... – Kirian CAUMES Jun 28 '19 at 18:10
  • What type of script is that? Does the script simply access MySQL data and that's all it needs from the MySQL container? If so, you can install python or any other dependency by adding another RUN line. In case you want to use alpine as a base image and install python 3, you would add a line `RUN apk add python3`. If your base image uses another package manager or you want to install other packages, adapt accordingly. – fjc Jun 30 '19 at 05:51
  • It may be helpful to also include a `depends_on` statement, so that the msyql boots up before the cron container, or to ensure that the container is ready, there is a [control startup recommendation](https://docs.docker.com/compose/startup-order/). – k0pernikus Jul 02 '19 at 09:11
  • I have the `depends_on` statement included in the `cron` service, would there be anything more required? – fjc Jul 02 '19 at 09:13
3

I recommend the solution provided by fjc. Treat this as nice-to-know to understand why your approach is not working.


Docker has RUN commands that are only being executed during build. Not on container startup.

It also has a CMD (or ENTRYPOINT) for executing specific scripts.

Since you are using mariadb your CMD it is:

ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 3306
CMD ["mysqld"]

(You can find the link to the actual dockerfles on dockerhub.)

This tells docker to run:

docker-entrypoint.sh mysqld

on startup.

You'd have to override its docker-entrypoint.sh to allow for the startup of the cron job as well.


See the relevant part of the Dockerfile for the CMD instruction:

CMD The CMD instruction has three forms:

CMD ["executable","param1","param2"] (exec form, this is the preferred form) CMD ["param1","param2"] (as default parameters to ENTRYPOINT) CMD command param1 param2 (shell form) There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

The main purpose of a CMD is to provide defaults for an executing container. These defaults can include an executable, or they can omit the executable, in which case you must specify an ENTRYPOINT instruction as well.

Note: If CMD is used to provide default arguments for the ENTRYPOINT instruction, both the CMD and ENTRYPOINT instructions should be specified with the JSON array format.

Note: The exec form is parsed as a JSON array, which means that you must use double-quotes (“) around words not single-quotes (‘).

Note: Unlike the shell form, the exec form does not invoke a command shell. This means that normal shell processing does not happen. For example, CMD [ "echo", "$HOME" ] will not do variable substitution on $HOME. If you want shell processing then either use the shell form or execute a shell directly, for example: CMD [ "sh", "-c", "echo $HOME" ]. When using the exec form and executing a shell directly, as in the case for the shell form, it is the shell that is doing the environment variable expansion, not docker.

When used in the shell or exec formats, the CMD instruction sets the command to be executed when running the image.

If you use the shell form of the CMD, then the will execute in /bin/sh -c:

FROM ubuntu CMD echo "This is a test." | wc - If you want to run your without a shell then you must express the command as a JSON array and give the full path to the executable. This array form is the preferred format of CMD. Any additional parameters must be individually expressed as strings in the array:

FROM ubuntu CMD ["/usr/bin/wc","--help"] If you would like your container to run the same executable every time, then you should consider using ENTRYPOINT in combination with CMD. See ENTRYPOINT.

If the user specifies arguments to docker run then they will override the default specified in CMD.

Note: Don’t confuse RUN with CMD. RUN actually runs a command and commits the result; CMD does not execute anything at build time, but specifies the intended command for the image.

Community
  • 1
  • 1
k0pernikus
  • 60,309
  • 67
  • 216
  • 347