Configue and deploy Kubernetes CronJobs from ruby.
Add this line to your application's Gemfile:
gem "cron-kubernetes"
And then execute:
$ bundle
Or install it yourself as:
$ gem install cron_kubernetes
You can configure global settings for your cron jobs. Add a file to your source like the example
below. If you are using Rails, you can add this to something like config/initializers/cron_kubernetes.rb
.
You must configure the identifier
and manifest
settings. The other settings are optional
and default values are shown below.
CronKubernetes.configuration do |config|
# Required
config.identifier = "my-application"
config.manifest = YAML.load_file(File.join(Rails.root, "deploy", "kubernetes-job.yml"))
# Optional
config.output = nil
config.job_template = %w[/bin/bash -l -c :job]
end
Provide an identifier for this schedule. For example, you might use your application name.
This is used by CronKubernetes
to know which CronJobs are associated with this schedule
so you should make sure it's unique within your cluster.
identifier
must be valid for a Kubernetes resource name and label value. Specifically,
lowercase alphanumeric characters ([a-z0-9A-Z]
), -
, and .
, and 63 characters or less.
This is a Kubernetes Job manifest used as the job template within the Kubernetes CronJob. That is, this is the job that's started at the specified schedule. For example:
apiVersion: batch/v1
kind: Job
metadata:
name: scheduled-job
spec:
template:
metadata:
name: scheduled-job
spec:
containers:
- name: my-shell
image: ubuntu
restartPolicy: OnFailure
In the example above we show the manifest loading a file, just to make it
simple. But you could also read use a HEREDOC, parse a template and insert
values, or anything else you want to do in the method, as long as you return
a valid Kubernetes Job manifest as a Hash
.
When the job is run, the default command in the Docker instance is replaced with the command specified in the cron schedule (see below). The command is run on the first container in the pod.
By default no redirection is done; cron behaves as normal. If you would like you
can specify an option here to redirect as you would on a shell command. For example,
"2>&1
to collect STDERR in STDOUT or >> /var/log/task.log
to append to a log file.
This is a template that we use to execute your rake, rails runner, or shell command in the container. The default template executes it in a login shell so that environment variables (and profile) are loaded.
You can modify this. The value should be an array with a command and arguments that will
replace both ENTRYPOINT
and CMD
in the Docker image. See
Define a Command and Arguments for a Container
for a discussion of how command
works in Kubernetes.
The gem will automatically connect to the Kubernetes server in the following cases:
- You are running this in a standard Kubernetes cluster
- You are running on a system with
kubeclient
installed and- the default cluster context has credentials
- the default cluster is GKE and your system has Google application default credentials installed
There are many other ways to connect and you can do so by providing your own
configured kubeclient
:
# config/initializers/resque-kubernetes.rb
CronKubernetes.configuration do |config|
config.kubeclient = Kubeclient::Client.new("http://localhost:8080/apis/batch", "v1")
end
Because this uses the CronJob
resource, make sure to connect to the /apis/batch
API endpoint and
API version v1
in your client.
Add a file to your source that defines the scheduled tasks. If you are using Rails, you could
put this in config/initializers/cron_kuberentes.rb
. Or, if you want to make it work like the
whenever
gem you could add these lines to config/schedule.rb
and then require
that from your
initializer.
CronKubernetes.schedule do
command "ls -l", schedule: "0 0 1 1 *"
rake "audit:state", schedule: "0 20 1 * *", name: "audit-state"
runner "CleanSweeper.run", schedule: "30 3 * * *"
end
For all jobs the command will change directories to either Rails.root
if Rails is installed
or the current working directory. These are evaluated when the scheduled tasks are loaded.
For all jobs you may provide a name
that will be used with the identifier
to name the
CronJob. If you do not provide a name CronKubernetes
will try to figure one out from the job and
pod templates plus a hash of the schedule and command.
A command
runs any arbitrary shell command on a schedule. The first argument is the command to run.
A rake
call runs a rake
task on the schedule. Rake and Bundler must be installed and on the path
in the container. The command it executes is bundle exec rake ...
.
A runner
runs arbitrary ruby code under Rails. Rails must be installed at bin/rails
from the
working folder. The command it executes is bin/rails runner '...'
.
Once you have configuration and cluster, then you can run the cron_kubernetes
command
to update your cluster.
bundle exec cron_kubernetes --configuration config/initializers/cron_kubernetes.rb --schedule config/schedule.rb
The command will read the provided configuration and current schedule, compare to any
CronJobs already in your cluster for this project (base on the identifier
) and then
add/remove/update the CronJobs to bring match the schedule.
You can provide either --configuration
or --schedule
, as long as between the files you have
loaded both a configuration and a schedule. For example, if they are in the same file, you would
just pass a single value:
bundle exec cron_kubernetes --schedule schedule.rb
If you are running in a Rails application where the initializers are auto-loaded, and your schedule is defined in (or in a file required by) your initializer, you could run this within your Rails environment:
bin/rails runner cron_kubernetes
- In place of
schedule
, supportevery
/at
syntax:every: :minute, :hour, :day, :month, :year 3.minutes, 1.hour, 1.day, 1.week, 1.month, 1.year at: "[H]H:mm[am|pm]"
Bug reports and pull requests are welcome on GitHub at https://github.com/keylime-toolbox/cron-kubernetes-ruby.
- Fork it (
https://github.com/[my-github-username]/cron-kubernetes-ruby/fork
) - Create your feature branch (
git checkout -b my-new-feature
) - Test your changes with
rake
, add new tests if needed - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Open a new Pull Request
After checking out the repo, run bin/setup
to install dependencies. Then,
run rake
to run the test suite.
You can run bin/console
for an interactive prompt that will allow you to
experiment.
Write test for any code that you add. Test all changes by running bin/rake
.
This does the following, which you can also run separately while working.
- Run unit tests:
appraisal rake spec
- Make sure that your code matches the styles:
rubocop
- Verify if any dependent gems have open CVEs (you must update these):
rake bundle:audit
To release a new version, update the version number in
lib/cron_kubernetes/version.rb
and the CHANGELOG.md
, then run
bundle exec rake release
, which will create a git tag for the version,
push git commits and tags, and push the .gem
file to
rubygems.org.
We have used the whenever
gem for years and we love it.
Much of the ideas for scheduling here were inspired by the great work that @javan and team
have put into that gem.
The gem is available as open source under the terms of the MIT License.