angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Voer een Docker commando uit in een Docker Cron container.

De Alpine Docker image maakt het zeer eenvoudig om een Cron-container te bouwen.

18 april 2023
In Docker
post main image
https://unsplash.com/@unsplash

Wanneer u Docker gebruikt, bestaat uw applicatie meestal uit meerdere Docker containers. Vaak wilt u in deze containers op bepaalde momenten scripts uitvoeren, bijvoorbeeld elke vijf minuten, eens per uur, eens per dag.

Dit is waar de taakplanner Cron om de hoek komt kijken, en er zijn verschillende opties om dit te doen. In deze post maak ik een aparte Cron container, en gebruik het Docker Exec commando om commando's en scripts uit te voeren in een andere container.

Ik had een werkende oplossing met de Ubuntu base image, maar wilde een eenvoudigere setup en een kleinere footprint. Daarom gebruik ik hier de Alpine Docker image . Zoals altijd draai ik dit op Ubuntu 22.04.

Alpine en Cron

Alpine heeft Cron al ingesteld. Binnen de container zijn er mappen waar we onze scripts kunnen plaatsen:

docker run alpine:3.17 ls -l /etc/periodic

Resultaat:

/etc/periodic:
total 20
drwxr-xr-x    2 root     root          4096 Mar 29 14:45 15min
drwxr-xr-x    2 root     root          4096 Mar 29 14:45 daily
drwxr-xr-x    2 root     root          4096 Mar 29 14:45 hourly
drwxr-xr-x    2 root     root          4096 Mar 29 14:45 monthly
drwxr-xr-x    2 root     root          4096 Mar 29 14:45 weekly

Scripts in deze directories, worden als volgt (periodiek) uitgevoerd door Cron:

docker run alpine:3.17 crontab -l

Resultaat:

# do daily/weekly/monthly maintenance
# min	hour	day	month	weekday	command
*/15	*	*	*	*	run-parts /etc/periodic/15min
0	*	*	*	*	run-parts /etc/periodic/hourly
0	2	*	*	*	run-parts /etc/periodic/daily
0	3	*	*	6	run-parts /etc/periodic/weekly
0	5	1	*	*	run-parts /etc/periodic/monthly

Met behulp van Docker Volumes, kunnen we deze directories mappen (mounten) op onze host. Waarschijnlijk wilt u sommige commando's of scripts op andere momenten uitvoeren, bijvoorbeeld:

  • Elke minuut.
  • Elk uur tussen 6 en 23.
  • Om middernacht.

Dit betekent dat we regels moeten toevoegen aan ons crontab-bestand en nieuwe directories moeten aanmaken in de container. We kunnen dit doen in een opstartbestand of in de Dockerfile. Bijvoorbeeld om een regel toe te voegen voor een directory met scripts die elk uur tussen 6 en 23 zullen worden uitgevoerd in de Dockerfile:

RUN echo "0       6-23    *       *       *       run-parts /etc/periodic/hourly_0600_2300" >> /etc/crontabs/root

We willen ook Bash gebruiken. En om Docker commando's te kunnen draaien in onze container die kunnen verwijzen naar andere Docker containers, voegen we Docker toe aan onze Dockerfile en een volume mapping in ons docker-compose.yml bestand:

RUN apk add --update --no-cache bash && \
    apk add --update --no-cache docker && \
    ...
    volumes:
      # access docker containers outside this container
      - /var/run/docker.sock:/var/run/docker.sock

Directories en bestanden

Hier is een boomstructuur van alle mappen en bestanden in het project:

├── project
│   ├── cron
│   │   ├── jobs
│   │   │   ├── 15min
│   │   │   ├── 1min
│   │   │   │   └── run_every_minute
│   │   │   ├── 5min
│   │   │   │   └── run_every_5_minutes
│   │   │   ├── daily
│   │   │   ├── daily_0000
│   │   │   ├── hourly
│   │   │   │   └── run_every_hour
│   │   │   └── hourly_0600_2300
│   │   │       └── run_every_hour_0600_2300
│   │   └── Dockerfile
│   ├── docker-compose.yml
│   └── .env

Voor het testen heb ik enkele Bash-scripts toegevoegd. Belangrijk: Volgens de Alpine documentatie: "Gebruik geen punten in uw scriptbestandsnamen - dit voorkomt dat ze werken.

- run_every_minute
- run_every_5_minutes
- run_every_hour
- run_every_hour_0600_2300

De inhoud van al deze scripts is hetzelfde:

#!/bin/bash
script_name=`basename "$0"`
echo "Running ${script_name} ..."

Zorg ervoor, dat al deze Bash-scripts uitvoerbaar zijn, bijvoorbeeld:

chmod a+x run_every_5_minutes

De bestanden

Hier zijn de andere bestanden in het project:

  • Dockerfile
  • docker-compose.yml
  • .env

Bestand: Dockerfile

Merk op dat Cron als root moet draaien, niet als user.

# Dockerfile
FROM alpine:3.17
MAINTAINER Peter Mooring peterpm@xs4all.nl peter@petermooring.com

# Install required packages
RUN apk add --update --no-cache bash && \
    apk add --update --no-cache docker && \
    # every minute
    echo "*       *       *       *       *       run-parts /etc/periodic/1min" >> /etc/crontabs/root && \
	# every 5 minutes
    echo "*/5     *       *       *       *       run-parts /etc/periodic/5min" >> /etc/crontabs/root && \
	# every hour between 06:00 and 23:00
    echo "0       6-23    *       *       *       run-parts /etc/periodic/hourly_0600_2300" >> /etc/crontabs/root && \
	# every day at 00:00
    echo "0       0       *       *       *       run-parts /etc/periodic/daily_0000" >> /etc/crontabs/root

# show all run-parts
# RUN crontab -l

WORKDIR /

File: docker-compose.yml

Merk op dat we de context instellen op './cron'. In deze map staan alle bestanden die bij deze container horen.

# docker-compose.yml
version: "3.7"

x-service_defaults: &service_defaults
  env_file:
    - ./.env
  restart: always
  logging:
    driver: json-file
    options:
      max-size: "10m"
      max-file: "5"

services:
  cron:
    << : *service_defaults
    build:
      context: ./cron
      dockerfile: Dockerfile
    volumes:
      - ./cron/jobs/1min:/etc/periodic/1min/:ro
      - ./cron/jobs/5min:/etc/periodic/5min/:ro
      - ./cron/jobs/15min:/etc/periodic/15min/:ro
      - ./cron/jobs/hourly:/etc/periodic/hourly/:ro
      - ./cron/jobs/hourly_0600_2300:/etc/periodic/hourly_0600_2300/:ro
      - ./cron/jobs/daily:/etc/periodic/daily/:ro
      - ./cron/jobs/daily_0000:/etc/periodic/daily_0000/:ro
      # access docker containers outside this container
      - /var/run/docker.sock:/var/run/docker.sock
    command: crond -f -l 8

Bestand: .env

In het bestand .env geven we alleen de projectnaam op:

# .env
COMPOSE_PROJECT_NAME=myapp

Bouwen, starten en enkele tests

Om de container te (her)bouwen:

docker-compose build --no-cache

Om de Cron-container te starten:

docker-compose up

De uitvoer zal iets zijn als:

Attaching to myapp_cron_1
cron_1  | Running run_every_minute ...
cron_1  | Running run_every_minute ...
cron_1  | Running run_every_minute ...
cron_1  | Running run_every_minute ...
cron_1  | Running run_every_minute ...
cron_1  | Running run_every_5_minutes ...
cron_1  | Running run_every_minute ...
...

De naam van de Cron-container is:

myapp_cron_1

Om de logs van de container te tonen:

docker logs -t -f myapp_cron_1

Resultaat:

2023-04-18T11:52:00.871554414Z Running run_every_minute ...
2023-04-18T11:53:00.872386069Z Running run_every_minute ...
2023-04-18T11:54:00.872337812Z Running run_every_minute ...
2023-04-18T11:55:00.872792007Z Running run_every_minute ...
2023-04-18T11:55:00.872825556Z Running run_every_5_minutes ...
2023-04-18T11:56:00.875379199Z Running run_every_minute ...

Om te controleren welke scripts worden uitgevoerd vanuit de directory ./cron/jobs/hourly_0600_2300, voer je het volgende commando uit in een andere terminal:

docker exec myapp_cron_1 run-parts --test /etc/periodic/hourly_0600_2300

Dit zal de scripts niet uitvoeren! Resultaat:

/etc/periodic/hourly_0600_2300/run_every_hour_0600_2300

Een script of commando uitvoeren in een andere Docker container

Laten we een commando uitvoeren in een BusyBox-container. Start in een andere terminal deze BusyBox-container op en geef hem de naam 'my_busybox':

docker run -it --name my_busybox --rm busybox

Maak een nieuw script 'run_busybox_env' in de directory './cron/jobs/1min' en maak het uitvoerbaar:

#!/bin/bash
# run_busybox_env
DOCKER=/usr/bin/docker

${DOCKER} exec my_busybox env

Dit script voert het 'env'-commando uit in de 'my_busybox'-container en geeft een overzicht van de enviromentvariabelen van de BusyBox-container.
Binnen een minuut zou het resultaat moeten verschijnen in onze Cron-container:

cron_1  | Running run_every_minute ...
cron_1  | Running run_every_5_minutes ...
cron_1  | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cron_1  | HOSTNAME=dc74e9704d49
cron_1  | HOME=/root
cron_1  | Running run_every_minute ...

HOSTNAME heeft de ContainerId van de BusyBox container, dit zal in uw geval verschillen. U kunt de ContainerId van de BusyBox container krijgen door te draaien:

docker ps | grep my_busybox

Samenvatting

Het gebruik van de Alpine Docker image voor onze Cron-container was zeer eenvoudig. Alles werkte in één keer. Het resultaat is minder configuratie en een kleinere footprint.

Links / credits

Alpine Linux:FAQ
https://wiki.alpinelinux.org/wiki/Alpine_Linux:FAQ

CRON Jobs with Alpine Linux and Docker
https://lucshelton.com/blog/cron-jobs-with-alpine-linux-and-docker

Docker Tip #40: Running Cron Jobs on the Host vs. in a Container
https://nickjanetakis.com/blog/docker-tip-40-running-cron-jobs-on-the-host-vs-in-a-container

Running cron jobs in a Docker Alpine container
https://devopscell.com/cron/docker/alpine/linux/2017/10/30/run-cron-docker-alpine.html

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.