Voer een Docker commando uit in een Docker Cron container.
De Alpine Docker image maakt het zeer eenvoudig om een Cron-container te bouwen.
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
Recent
- Database UUID primaire sleutels van je webapplicatie verbergen
- Don't Repeat Yourself (DRY) met Jinja2
- SQLAlchemy, PostgreSQL, maximum aantal rijen per user
- Toon de waarden in SQLAlchemy dynamische filters
- Veilige gegevensoverdracht met Public Key versleuteling en pyNaCl
- rqlite: een alternatief voor SQLite met hoge beschikbaarheid en distributed
Meest bekeken
- Met behulp van Python's pyOpenSSL om SSL-certificaten die van een host zijn gedownload te controleren
- Gebruik van UUIDs in plaats van Integer Autoincrement Primary Keys met SQLAlchemy en MariaDb
- Maak verbinding met een dienst op een Docker host vanaf een Docker container
- PyInstaller en Cython gebruiken om een Python executable te maken
- SQLAlchemy: Gebruik van Cascade Deletes om verwante objecten te verwijderen
- Flask RESTful API verzoekparametervalidatie met Marshmallow-schema's