Article summary
My software development team recently helped a client migrate their database from a manually-maintained VM to a managed Azure service. The migration went smoothly, but there was one lingering chore: finding a new home for a cron job.
Once a week, the old database server would copy a database between environments with a command like this:
mysqldump (... parameters) | mysql (...parameters)
I definitely didn’t want to keep an idle VM around, doing 10 minutes of work each week. We need something easier and cheaper, preferably with little to no maintenance overhead. I looked through various Azure resource types that can host long-running tasks and eventually settled on one.
Container App Job
Azure’s Container App Job is built to be able to handle a large swarm of custom Dockerized microservices, but it also easily meets my need. It can load an arbitrary Docker image, run a custom command that takes several minutes, and then quit (and stop incurring costs).
You can get a feel for the available configuration by clicking around the azure portal:
But, I wound up iterating on it with a repeatable Azure CLI command:
az containerapp job create -n my-db-copier -g my-resource-group \
--trigger-type Schedule \
--cron-expression "0 2 * * 6" \
--replica-timeout 1800 \
--replica-retry-limit 0 \
--replica-completion-count 1 \
--parallelism 1 \
--image docker.io/mysql:8 \
--command "bash" \
--args "\-c" "set -o pipefail && mysqldump $SRC_DB_NAME ... | mysql ..." \
--env-vars "SRC_DB_NAME=example1" "SRC_HOST=example2" ... \
--workload-profile-name Consumption \
--environment "/subscriptions/abc123/resourceGroups/my-resource-group/providers/Microsoft.App/managedEnvironments/managedEnvironment-foobar-abc123"
I love that we’re still using cron expressions, some ~50 years later.
What I Like About It
In addition to running on a schedule, the job can be invoked manually, which is occasionally handy.
One of my goals was for this to be inexpensive. Thanks to a generous base quota, so far it’s been completely free.
Because the one-line command depends on a couple of tools readily available in a public Docker image, we don’t have to build (and won’t have to maintain) an image of our own. Overall, this feels like an insultingly simple task for a sophisticated platform, but it’s been working great!