From 37fd38b39118c8ba49b332226cdd35b78c970b42 Mon Sep 17 00:00:00 2001 From: Victor Castell <0x@vcastellm.xyz> Date: Mon, 12 Feb 2024 00:49:43 +0100 Subject: [PATCH] Docs v4 (#1473) * Tag v3 --- website/bun.lockb | Bin 615291 -> 613754 bytes website/docusaurus.config.js | 2 +- .../version-v3/basics/_category_.json | 4 + .../version-v3/basics/configuration.md | 54 +++ .../version-v3/basics/getting-started.md | 97 +++++ .../version-v3/basics/installation.md | 67 ++++ .../version-v3/cli/_category_.json | 3 + .../versioned_docs/version-v3/cli/dkron.md | 34 ++ .../version-v3/cli/dkron_agent.md | 113 ++++++ .../version-v3/cli/dkron_completion.md | 37 ++ .../version-v3/cli/dkron_completion_bash.md | 58 +++ .../version-v3/cli/dkron_completion_fish.md | 47 +++ .../cli/dkron_completion_powershell.md | 44 +++ .../version-v3/cli/dkron_completion_zsh.md | 54 +++ .../version-v3/cli/dkron_doc.md | 40 ++ .../version-v3/cli/dkron_keygen.md | 37 ++ .../version-v3/cli/dkron_leave.md | 38 ++ .../version-v3/cli/dkron_raft.md | 30 ++ .../version-v3/cli/dkron_raft_list-peers.md | 32 ++ .../version-v3/cli/dkron_raft_remove-peer.md | 33 ++ .../version-v3/cli/dkron_version.md | 35 ++ website/versioned_docs/version-v3/intro.md | 36 ++ .../version-v3/pro/_category_.json | 3 + .../versioned_docs/version-v3/pro/_index.md | 6 + website/versioned_docs/version-v3/pro/acls.md | 90 +++++ website/versioned_docs/version-v3/pro/auth.md | 13 + .../version-v3/pro/cli/_category_.json | 3 + .../version-v3/pro/cli/dkron.md | 34 ++ .../version-v3/pro/cli/dkron_agent.md | 125 +++++++ .../version-v3/pro/cli/dkron_completion.md | 37 ++ .../pro/cli/dkron_completion_bash.md | 58 +++ .../pro/cli/dkron_completion_fish.md | 47 +++ .../pro/cli/dkron_completion_powershell.md | 44 +++ .../pro/cli/dkron_completion_zsh.md | 60 +++ .../version-v3/pro/cli/dkron_doc.md | 40 ++ .../version-v3/pro/cli/dkron_keygen.md | 37 ++ .../version-v3/pro/cli/dkron_leave.md | 44 +++ .../version-v3/pro/cli/dkron_raft.md | 33 ++ .../pro/cli/dkron_raft_list-peers.md | 35 ++ .../pro/cli/dkron_raft_remove-peer.md | 36 ++ .../version-v3/pro/cli/dkron_version.md | 35 ++ .../version-v3/pro/commercial-faq.md | 72 ++++ .../version-v3/pro/commercial-support.md | 9 + .../version-v3/pro/configuration.md | 14 + .../version-v3/pro/encryption.md | 25 ++ .../version-v3/pro/executors/_category_.json | 3 + .../version-v3/pro/executors/docker.md | 21 ++ .../version-v3/pro/executors/ecs.md | 82 +++++ .../versioned_docs/version-v3/pro/failover.md | 7 + .../version-v3/pro/processors/_category_.json | 3 + .../pro/processors/elasticsearch.md | 50 +++ .../version-v3/pro/processors/email.md | 34 ++ .../version-v3/pro/processors/slack.md | 32 ++ .../version-v3/upgrading/_category_.json | 3 + .../version-v3/upgrading/from_v1_to_v2.md | 43 +++ .../version-v3/upgrading/from_v2_0_to_v2_2.md | 9 + .../version-v3/usage/_category_.json | 4 + .../version-v3/usage/chaining.md | 31 ++ .../version-v3/usage/cloud-auto-join.md | 342 ++++++++++++++++++ .../version-v3/usage/clustering.md | 89 +++++ .../version-v3/usage/concurrency.md | 27 ++ .../version-v3/usage/cron-spec.md | 114 ++++++ .../version-v3/usage/cronitor.md | 29 ++ .../versioned_docs/version-v3/usage/ecs.md | 26 ++ .../version-v3/usage/executors/grpc.md | 33 ++ .../version-v3/usage/executors/http.md | 40 ++ .../version-v3/usage/executors/index.md | 9 + .../version-v3/usage/executors/kafka.md | 29 ++ .../version-v3/usage/executors/nats.md | 32 ++ .../version-v3/usage/executors/shell.md | 42 +++ .../version-v3/usage/metatags.md | 24 ++ .../version-v3/usage/metrics.md | 73 ++++ .../version-v3/usage/plugins/_category_.json | 3 + .../version-v3/usage/plugins/develop.md | 36 ++ .../version-v3/usage/plugins/index.md | 20 + .../version-v3/usage/processors/file.md | 33 ++ .../version-v3/usage/processors/index.md | 26 ++ .../version-v3/usage/processors/log.md | 29 ++ .../version-v3/usage/processors/syslog.md | 31 ++ .../version-v3/usage/recovery.md | 104 ++++++ .../version-v3/usage/retries.md | 22 ++ .../version-v3/usage/storage.md | 7 + .../version-v3/usage/target-nodes-spec.md | 120 ++++++ .../version-v3/usage/upgrade.md | 26 ++ .../version-v3-sidebars.json | 8 + website/versions.json | 1 + 86 files changed, 3491 insertions(+), 1 deletion(-) create mode 100644 website/versioned_docs/version-v3/basics/_category_.json create mode 100644 website/versioned_docs/version-v3/basics/configuration.md create mode 100644 website/versioned_docs/version-v3/basics/getting-started.md create mode 100644 website/versioned_docs/version-v3/basics/installation.md create mode 100644 website/versioned_docs/version-v3/cli/_category_.json create mode 100644 website/versioned_docs/version-v3/cli/dkron.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_agent.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_completion.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_completion_bash.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_completion_fish.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_completion_powershell.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_completion_zsh.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_doc.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_keygen.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_leave.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_raft.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_raft_list-peers.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_raft_remove-peer.md create mode 100644 website/versioned_docs/version-v3/cli/dkron_version.md create mode 100644 website/versioned_docs/version-v3/intro.md create mode 100644 website/versioned_docs/version-v3/pro/_category_.json create mode 100644 website/versioned_docs/version-v3/pro/_index.md create mode 100644 website/versioned_docs/version-v3/pro/acls.md create mode 100644 website/versioned_docs/version-v3/pro/auth.md create mode 100644 website/versioned_docs/version-v3/pro/cli/_category_.json create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_agent.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_completion.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_completion_bash.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_completion_fish.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_completion_powershell.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_completion_zsh.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_doc.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_keygen.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_leave.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_raft.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_raft_list-peers.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_raft_remove-peer.md create mode 100644 website/versioned_docs/version-v3/pro/cli/dkron_version.md create mode 100644 website/versioned_docs/version-v3/pro/commercial-faq.md create mode 100644 website/versioned_docs/version-v3/pro/commercial-support.md create mode 100644 website/versioned_docs/version-v3/pro/configuration.md create mode 100644 website/versioned_docs/version-v3/pro/encryption.md create mode 100644 website/versioned_docs/version-v3/pro/executors/_category_.json create mode 100644 website/versioned_docs/version-v3/pro/executors/docker.md create mode 100644 website/versioned_docs/version-v3/pro/executors/ecs.md create mode 100644 website/versioned_docs/version-v3/pro/failover.md create mode 100644 website/versioned_docs/version-v3/pro/processors/_category_.json create mode 100644 website/versioned_docs/version-v3/pro/processors/elasticsearch.md create mode 100644 website/versioned_docs/version-v3/pro/processors/email.md create mode 100644 website/versioned_docs/version-v3/pro/processors/slack.md create mode 100644 website/versioned_docs/version-v3/upgrading/_category_.json create mode 100644 website/versioned_docs/version-v3/upgrading/from_v1_to_v2.md create mode 100644 website/versioned_docs/version-v3/upgrading/from_v2_0_to_v2_2.md create mode 100644 website/versioned_docs/version-v3/usage/_category_.json create mode 100644 website/versioned_docs/version-v3/usage/chaining.md create mode 100644 website/versioned_docs/version-v3/usage/cloud-auto-join.md create mode 100644 website/versioned_docs/version-v3/usage/clustering.md create mode 100644 website/versioned_docs/version-v3/usage/concurrency.md create mode 100644 website/versioned_docs/version-v3/usage/cron-spec.md create mode 100644 website/versioned_docs/version-v3/usage/cronitor.md create mode 100644 website/versioned_docs/version-v3/usage/ecs.md create mode 100644 website/versioned_docs/version-v3/usage/executors/grpc.md create mode 100644 website/versioned_docs/version-v3/usage/executors/http.md create mode 100644 website/versioned_docs/version-v3/usage/executors/index.md create mode 100644 website/versioned_docs/version-v3/usage/executors/kafka.md create mode 100644 website/versioned_docs/version-v3/usage/executors/nats.md create mode 100644 website/versioned_docs/version-v3/usage/executors/shell.md create mode 100644 website/versioned_docs/version-v3/usage/metatags.md create mode 100644 website/versioned_docs/version-v3/usage/metrics.md create mode 100644 website/versioned_docs/version-v3/usage/plugins/_category_.json create mode 100644 website/versioned_docs/version-v3/usage/plugins/develop.md create mode 100644 website/versioned_docs/version-v3/usage/plugins/index.md create mode 100644 website/versioned_docs/version-v3/usage/processors/file.md create mode 100644 website/versioned_docs/version-v3/usage/processors/index.md create mode 100644 website/versioned_docs/version-v3/usage/processors/log.md create mode 100644 website/versioned_docs/version-v3/usage/processors/syslog.md create mode 100644 website/versioned_docs/version-v3/usage/recovery.md create mode 100644 website/versioned_docs/version-v3/usage/retries.md create mode 100644 website/versioned_docs/version-v3/usage/storage.md create mode 100644 website/versioned_docs/version-v3/usage/target-nodes-spec.md create mode 100644 website/versioned_docs/version-v3/usage/upgrade.md create mode 100644 website/versioned_sidebars/version-v3-sidebars.json diff --git a/website/bun.lockb b/website/bun.lockb index 7e3c16f419102dd12ac71681bc677402679dfd01..f3fdb0dc7eec1dca1c741f8f33d0be71a6d89d15 100755 GIT binary patch delta 108680 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%0AXvt=+=0#c<+>C0f)tG?^R zzWLA3Y(Dk7Y!^7!~*_QwdXmCi=X?ZyMcNGx4<<+i5_<<*P3pqSjE<+#6 zl9Ym;WK7@j_Y?28X9jjs8Q_|~S*&UXMb_GwaXWc9a|AL0Ih~YAjhZS>_R1Ea9ZwsbYaHj0gGF0d z8hzGOY3O+sX{J;vr~kBnC-rQGk?$}{?B%D{i~X*~ z_K+av5`2GeuLeUXwHdVc$WGeO3^>p4q|2xfz&|=>y!(aePF1V-3-j%r^}j-wzvqmM52mrb);LwtYD77oXj8>X!fk8wGNf#Ee7Ao8vCv+S3Ilb&@+A)B<#i9T$&#RTuXB4NEF&ypG!CK9*`w* zwx~AsR+zJIdZ`vF>WT(^>{BwJycDb2{#Jj%X++4EmfVrnB#dUz#gwW^? zif_QX$9KOU7Uuv?zX@7$SFWD$Ma%D=U{_djw&!nj8JJ3A2fD`W0JogASAM6>9noXR zRb(f)3|BiUegU)d#7HNPKdf7~?ASqRw;)_sei!uaGX1LJhOV?Tt3-7=D^>NWj|sG*FBl-5 zI_bvk=&XDumBA?>;SXUgR&O47>TB)Ia8(1;^NYzS;fodg;xA@C*ms#HBDq9GFNbvj zk-uisj4l}O1%J?9euu7{!8>%tqN_ny;AbXu1&@Tv)GLdjm0F{#mLvFn_NC=-vE{e% zQj?FP!G}z;i~-?Bi2^bwYZ`S&k#50rVIrHSPaQga%&)%~$oq+AGr%bTaKh2ZU#8yL zlA&H*AesDt8utWp<50i<$fldJ-MB>wA)^2VUVSx5jRcXYVf`o*frc{PP#--wm#V~p zo-`zRVaI>+lV5j452^CHjM`as&?Vv_6xAJ-C3Sf;h2MN>5gw&wMaj;T?1dV^*q?HH z!bpl%_0)=ZMv9Mz+ict`*=k8Nd~XJmETbTGrR0jq#=~hi!^!x{QWC=?+z8soaFUS{ z`z$JtVJIyz%y^sxC;ksCkj)u)P0*?*<>SjrLnioHS_uM+4Fc>;FTr;?H`J!^-by1j zhm(6lXGWi+g}tFOr6nnAZO@ZyA52rV&x=4qmrnifr-Uj0io-BCuqILGsU08!h5&-^ zQhA!3dAVk`c@+ah3QE`;UNY~@x`MHes|+P+=V>YG;?RSQgzf+UJpefRPElms!NFe| z0EPLWGJO?4ZQ~2nurKJ9t45hrEl_BqFVNn;!1yN5nU}<_pI;E($$ zYK!t|F>q>?0;@^9U?|Q<+)eP7 zHoa#{rs7b{vC2*AITTKB<(u@)P{l**d6PB_#W|NL+3}?X>aNT{efSK1bJ5Av+oX_Q zeUAB9pIN_>yoUi)D)f6A(_dKNOfJKf&s)$;n*fj z>OE-a=t0B!wdj7b#(mGsnwL!|d8Fc|#d9H^Veo8JAN)e+ajUwfdpMuNJFmH?RO)P1 zZ0~mwqpF77{1ViwW8+Z`|2h{catUXp7tj5<`H&WF^xsHqw7QXVlHy(^eujWP{LPk^ z2imn?GXn2ytgabl=( z&ux31=p4ZMESV$};Ly*F8idrhT=6#8+vRU>oV3Bl1uIZ{ujsqI%xQTX-nom*b{*W} zU~{F_pmd7;Lh-Uai2WjVrDr|$*f;ONs1?}fTKEN6h39TO5eIzk3&mY)bC{m)zD zAF3N~Q8g=ejMCCZQ$K6vOeMk|&)Z8B2P?|iU>=|4zCkIfPRDzhSD;D@6f5%hQ;DJJ zj}#C7rqHrKmC0GwCzU4}O6g-^mR0?<;$0D;5J>}Z&yBxDrL4s3O0ESB-ez8dJ})%4 z%^G}P@y*V1-)J7FWTk8~pKVIHBh0I*$g*1RH22Yv-A?nstns_dLzJwW`_1Q+r?_qA omgIY1QOQuU#vCzEisB0PS&=RlL1jSi*KQUwG+7ZI7J*Iw2TKE`2mk;8 delta 109766 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 diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 325c44ffe..84b0162b9 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -68,7 +68,7 @@ const config = { lastVersion: 'current', versions: { current: { - label: 'v3', + label: 'v4', path: '', }, }, diff --git a/website/versioned_docs/version-v3/basics/_category_.json b/website/versioned_docs/version-v3/basics/_category_.json new file mode 100644 index 000000000..daec3e16a --- /dev/null +++ b/website/versioned_docs/version-v3/basics/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Basics", + "position": 1 +} diff --git a/website/versioned_docs/version-v3/basics/configuration.md b/website/versioned_docs/version-v3/basics/configuration.md new file mode 100644 index 000000000..878b7ee51 --- /dev/null +++ b/website/versioned_docs/version-v3/basics/configuration.md @@ -0,0 +1,54 @@ +# Configuration +## Configuration sources + +Settings can be specified in three ways (in order of precedence): + +1. Command line arguments. +1. Environment variables starting with **`DKRON_`** +1. **`dkron.yml`** config file + +:::caution +Dkron sends anonymous usage data to a server with the purpose of elaborating usage statistics, if you want to disable statistics collection, you can disable it in the dkron config file or in the command line using `--disable-usage-stats` parameter +::: + +## Config file location + +Config file will be loaded from the following paths: + +- `/etc/dkron` +- `$HOME/.dkron` +- `./config` + +### Config file example + +```yaml +# Dkron example configuration file +# server: false +# bootstrap-expect: 3 +# data-dir: dkron.data +# log-level: debug +# tags: +# dc: east +# encrypt: a-valid-key-generated-with-dkron-keygen +# retry-join: +# - 10.0.0.1 +# - 10.0.0.2 +# - 10.0.0.3 +# raft-multiplier: 1 +# webhook-url: https://hooks.slack.com/services/XXXXXX/XXXXXXX/XXXXXXXXXXXXXXXXXXXX +# webhook-payload: "payload={\"text\": \"{{.Report}}\", \"channel\": \"#foo\"}" +# webhook-headers: Content-Type:application/x-www-form-urlencoded +# mail-host: email-smtp.eu-west-1.amazonaws.com +# mail-port: 25 +# mail-username": mailuser +# mail-password": mailpassword +# mail-from": cron@example.com +# mail-subject_prefix: [Dkron] +``` + +### SEE ALSO + +* [dkron agent](/docs/cli/dkron_agent/) - Start a dkron agent +* [dkron doc](/docs/cli/dkron_doc/) - Generate Markdown documentation for the Dkron CLI. +* [dkron keygen](/docs/cli/dkron_keygen/) - Generates a new encryption key +* [dkron version](/docs/cli/dkron_version/) - Show version diff --git a/website/versioned_docs/version-v3/basics/getting-started.md b/website/versioned_docs/version-v3/basics/getting-started.md new file mode 100644 index 000000000..46c3ab0a0 --- /dev/null +++ b/website/versioned_docs/version-v3/basics/getting-started.md @@ -0,0 +1,97 @@ +--- +sidebar_position: 1 +--- +# Getting started + +## Introduction + +Dkron nodes can work in two modes, agents or servers. + +A Dkron agent is a cluster member that can handle job executions, run your scripts and return the resulting output to the server. + +A Dkron server is also a cluster member that send job execution queries to agents or other servers, so servers can execute jobs too. + +The main distinction is that servers order job executions, can be used to schedule jobs, handles data storage and participate on leader election. + +Dkron clusters have a leader, the leader is responsible of starting job execution queries in the cluster. + +Any Dkron agent or server acts as a cluster member and it's available to run scheduled jobs. + +Default is for all nodes to execute each job. You can control what nodes run a job by specifying tags and a count of target nodes having this tag. This gives an unprecedented level of flexibility in runnig jobs across a cluster of any size and with any combination of machines you need. + +All the execution responses will be gathered by the scheduler and stored in the database. + +## State storage + +Dkron deployment is just a single binary, it stores the state in an internal BuntDB instance and replicate all changes between all server nodes using the Raft protocol, it doesn't need any other storage system outside itself. + +## Installation + +See the [installation](installation.md). + +## Configuration + +See the [configuration](configuration.md). + +## Usage + +By default Dkron uses the following ports: + +- `8946` for serf layer between agents +- `8080` for HTTP for the API and Dashboard +- `6868` for gRPC and raft layer comunication between agents. + +:::info +Be sure you have opened this ports (or the ones that you configured) in your firewall or AWS security groups. +::: + +### Starting a single node + +Works out of the box, good for non HA installations. + +- System service: If no changes are done to the default config files, dkron will start as a service in single mode. +- Command line: Running a single node with default config can be done by running: + +``` +dkron agent --server --bootstrap-expect=1 +``` + +Check your server is working: `curl localhost:8080/v1` + +:::info +For a full list of configuration parameters and its description, see the [CLI docs](/docs/cli/dkron_agent) +::: + +### Create a Job + +:::info +This job will only run in just one `server` node due to the node count in the tag. Refer to the [target node spec](/docs/usage/target-nodes-spec) for details. +::: + +```bash +curl localhost:8080/v1/jobs -XPOST -d '{ + "name": "job1", + "schedule": "@every 10s", + "timezone": "Europe/Berlin", + "owner": "Platform Team", + "owner_email": "platform@example.com", + "disabled": false, + "tags": { + "server": "true:1" + }, + "metadata": { + "user": "12345" + }, + "concurrency": "allow", + "executor": "shell", + "executor_config": { + "command": "date" + } +}' +``` + +For full Job params description refer to the Job model in the [API guide](/api) + +That's it! + +#### To start configuring an HA installation of Dkron follow the [clustering guide](/docs/usage/clustering) diff --git a/website/versioned_docs/version-v3/basics/installation.md b/website/versioned_docs/version-v3/basics/installation.md new file mode 100644 index 000000000..4fb7728e7 --- /dev/null +++ b/website/versioned_docs/version-v3/basics/installation.md @@ -0,0 +1,67 @@ +# Installation +## Running the binary + +Download the packaged archive for your platform from the [downloads page](https://github.com/distribworks/dkron/releases) and extract the package to a shared location in your drive, like /opt/local/bin. + +Run Dkron with default setting: `dkron agent --server --bootstrap-expect=1` + +Navigate to http://localhost:8080/ui + +## Installing the package + +### Debian repo + +APT repository: +``` +deb [trusted=yes] https://repo.distrib.works/apt/ / +``` + +Then install: `sudo apt-get install dkron` + +### YUM repo + +YUM repository: + +``` +[dkron] +name=Dkron Pro Private Repo +baseurl=https://repo.distrib.works/yum/ +enabled=1 +gpgcheck=0 +``` + +Then install: `sudo yum install dkron` + +This will start Dkron as a system service and the place example configuration file under `/etc/dkron/dkron.yml` + +## Running in Docker + +Dkron provides an official Docker image via Docker Hub that can be used for deployment on any system running Docker. + +### Launching Dkron as a new container + +Here’s a quick one-liner to get you off the ground (please note, we recommend further configuration for production deployments below): + +``` +docker run -d -p 8080:8080 --name dkron dkron/dkron agent --server --bootstrap-expect=1 --node-name=node1 +``` + +This will launch a Dkron server on port 8080 by default. You can use `docker logs -f dkron` to follow the rest of the initialization progress. Once the Dkron startup completes you can access the app at localhost:8080 + +Since Docker containers have their own ports and we just map them to the system ports as needed it’s easy to move Dkron onto a different system port if you wish. For example running Dkron on port 12345: + +``` +docker run -d -p 12345:8080 --name dkron dkron/dkron agent --server --bootstrap-expect=1 --node-name=node1 +``` + +### Mounting a mapped file storage volume + +Dkron uses the local filesystem for storing the embedded database to store its own application data and the Raft protocol log. The end result is that your Dkron data will be on disk inside your container and lost if you ever remove the container. + +To persist your data outside of the container and make it available for use between container launches we can mount a local path inside our container. + +``` +docker run -d -p 8080:8080 -v ~/dkron.data:/dkron.data --name dkron dkron/dkron agent --server --bootstrap-expect=1 --data-dir=/dkron.data +``` + +Now when you launch your container we are mounting that folder from our local filesystem into the container. diff --git a/website/versioned_docs/version-v3/cli/_category_.json b/website/versioned_docs/version-v3/cli/_category_.json new file mode 100644 index 000000000..e2c485445 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "CLI" +} diff --git a/website/versioned_docs/version-v3/cli/dkron.md b/website/versioned_docs/version-v3/cli/dkron.md new file mode 100644 index 000000000..5db686bb0 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron.md @@ -0,0 +1,34 @@ +--- +date: 2022-06-05 +title: "dkron" +slug: dkron +url: /cli/dkron/ +--- +## dkron + +Open source distributed job scheduling system + +### Synopsis + +Dkron is a system service that runs scheduled jobs at given intervals or times, +just like the cron unix service but distributed in several machines in a cluster. +If a machine fails (the leader), a follower will take over and keep running the scheduled jobs without human intervention. + +### Options + +``` + --config string config file path + -h, --help help for dkron +``` + +### SEE ALSO + +* [dkron agent](/docs/cli/dkron_agent/) - Start a dkron agent +* [dkron completion](/docs/cli/dkron_completion/) - Generate the autocompletion script for the specified shell +* [dkron doc](/docs/cli/dkron_doc/) - Generate Markdown documentation for the Dkron CLI. +* [dkron keygen](/docs/cli/dkron_keygen/) - Generates a new encryption key +* [dkron leave](/docs/cli/dkron_leave/) - Force an agent to leave the cluster +* [dkron raft](/docs/cli/dkron_raft/) - Command to perform some raft operations +* [dkron version](/docs/cli/dkron_version/) - Show version + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_agent.md b/website/versioned_docs/version-v3/cli/dkron_agent.md new file mode 100644 index 000000000..1591c68aa --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_agent.md @@ -0,0 +1,113 @@ +--- +date: 2022-06-05 +title: "dkron agent" +slug: dkron_agent +url: /cli/dkron_agent/ +--- +## dkron agent + +Start a dkron agent + +### Synopsis + +Start a dkron agent that schedules jobs, listens for executions and runs executors. +It also runs a web UI. + +``` +dkron agent [flags] +``` + +### Options + +``` + --advertise-addr string Address used to advertise to other nodes in the cluster. By default, + the bind address is advertised. The value supports + go-sockaddr/template format. + --advertise-rpc-port int Use the value of rpc-port by default + --bind-addr string Specifies which address the agent should bind to for network services, + including the internal gossip protocol and RPC mechanism. This should be + specified in IP format, and can be used to easily bind all network services + to the same address. The value supports go-sockaddr/template format. + (default "{{ GetPrivateIP }}:8946") + --bootstrap-expect int Provides the number of expected servers in the datacenter. Either this value + should not be provided or the value must agree with other servers in the + cluster. When provided, Dkron waits until the specified number of servers are + available and then bootstraps the cluster. This allows an initial leader to be + elected automatically. This flag requires server mode. + --cronitor-endpoint string Cronitor endpoint to call for notifications + --data-dir string Specifies the directory to use for server-specific data, including the + replicated log. By default, this is the top-level data-dir, + like [/var/lib/dkron] (default "dkron.data") + --datacenter string Specifies the data center of the local agent. All members of a datacenter + should share a local LAN connection. (default "dc1") + --disable-usage-stats Disable sending anonymous usage stats + --dog-statsd-addr string DataDog Agent address + --dog-statsd-tags strings Datadog tags, specified as key:value + --enable-prometheus Enable serving prometheus metrics + --encrypt string Key for encrypting network traffic. Must be a base64-encoded 16-byte key + -h, --help help for agent + --http-addr string Address to bind the UI web server to. Only used when server. The value + supports go-sockaddr/template format. (default ":8080") + --join strings An initial agent to join with. This flag can be specified multiple times + --log-level string Log level (debug|info|warn|error|fatal|panic) (default "info") + --mail-from string From email address to use + --mail-host string Mail server host address to use for notifications + --mail-password string Mail server password to use + --mail-payload string Notification mail payload + --mail-port uint16 Mail server port + --mail-subject-prefix string Notification mail subject prefix (default "[Dkron]") + --mail-username string Mail server username used for authentication + --node-name string Name of this node. Must be unique in the cluster (default "mariette.local") + --pre-webhook-endpoint string Pre-webhook endpoint to call for notifications + --pre-webhook-headers strings Headers to use when calling the pre-webhook. Can be specified multiple times + --pre-webhook-payload string Body of the POST request to send on pre-webhook call + --profile string Profile is used to control the timing profiles used (default "lan") + --raft-multiplier int An integer multiplier used by servers to scale key Raft timing parameters. + Omitting this value or setting it to 0 uses default timing described below. + Lower values are used to tighten timing and increase sensitivity while higher + values relax timings and reduce sensitivity. Tuning this affects the time it + takes to detect leader failures and to perform leader elections, at the expense + of requiring more network and CPU resources for better performance. By default, + Dkron will use a lower-performance timing that's suitable for minimal Dkron + servers, currently equivalent to setting this to a value of 5 (this default + may be changed in future versions of Dkron, depending if the target minimum + server profile changes). Setting this to a value of 1 will configure Raft to + its highest-performance mode is recommended for production Dkron servers. + The maximum allowed value is 10. (default 1) + --region string Specifies the region the Dkron agent is a member of. A region typically maps + to a geographic region, for example us, with potentially multiple zones, which + map to datacenters such as us-west and us-east (default "global") + --retry-interval string Time to wait between join attempts. (default "30s") + --retry-join strings Address of an agent to join at start time with retries enabled. + Can be specified multiple times. + --retry-max int Maximum number of join attempts. Defaults to 0, which will retry indefinitely. + --rpc-port int RPC Port used to communicate with clients. Only used when server. + The RPC IP Address will be the same as the bind address. (default 6868) + --serf-reconnect-timeout string This is the amount of time to attempt to reconnect to a failed node before + giving up and considering it completely gone. In Kubernetes, you might need + this to about 5s, because there is no reason to try reconnects for default + 24h value. Also Raft behaves oddly if node is not reaped and returned with + same ID, but different IP. + Format there: https://golang.org/pkg/time/#ParseDuration (default "24h") + --server This node is running in server mode + --statsd-addr string Statsd address + --tag strings Tag can be specified multiple times to attach multiple key/value tag pairs + to the given node, specified as key=value + --ui Enable the web UI on this node. The node must be server. (default true) + --webhook-endpoint string Webhook endpoint to call for notifications + --webhook-headers strings Headers to use when calling the webhook URL. Can be specified multiple times + --webhook-payload string Body of the POST request to send on webhook call + --webhook-url string Webhook url to call for notifications. Deprecated, use webhook-endpoint instead +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_completion.md b/website/versioned_docs/version-v3/cli/dkron_completion.md new file mode 100644 index 000000000..8902ee27e --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_completion.md @@ -0,0 +1,37 @@ +--- +date: 2022-06-05 +title: "dkron completion" +slug: dkron_completion +url: /cli/dkron_completion/ +--- +## dkron completion + +Generate the autocompletion script for the specified shell + +### Synopsis + +Generate the autocompletion script for dkron for the specified shell. +See each sub-command's help for details on how to use the generated script. + + +### Options + +``` + -h, --help help for completion +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system +* [dkron completion bash](/docs/cli/dkron_completion_bash/) - Generate the autocompletion script for bash +* [dkron completion fish](/docs/cli/dkron_completion_fish/) - Generate the autocompletion script for fish +* [dkron completion powershell](/docs/cli/dkron_completion_powershell/) - Generate the autocompletion script for powershell +* [dkron completion zsh](/docs/cli/dkron_completion_zsh/) - Generate the autocompletion script for zsh + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_completion_bash.md b/website/versioned_docs/version-v3/cli/dkron_completion_bash.md new file mode 100644 index 000000000..0ed0570d0 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_completion_bash.md @@ -0,0 +1,58 @@ +--- +date: 2022-06-05 +title: "dkron completion bash" +slug: dkron_completion_bash +url: /cli/dkron_completion_bash/ +--- +## dkron completion bash + +Generate the autocompletion script for bash + +### Synopsis + +Generate the autocompletion script for the bash shell. + +This script depends on the 'bash-completion' package. +If it is not installed already, you can install it via your OS's package manager. + +To load completions in your current shell session: + +``` + source <(dkron completion bash) +``` + +To load completions for every new session, execute once: + +#### Linux: + + dkron completion bash > /etc/bash_completion.d/dkron + +#### macOS: + + dkron completion bash > /usr/local/etc/bash_completion.d/dkron + +You will need to start a new shell for this setup to take effect. + + +``` +dkron completion bash +``` + +### Options + +``` + -h, --help help for bash + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron completion](/docs/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_completion_fish.md b/website/versioned_docs/version-v3/cli/dkron_completion_fish.md new file mode 100644 index 000000000..dd300df61 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_completion_fish.md @@ -0,0 +1,47 @@ +--- +date: 2022-06-05 +title: "dkron completion fish" +slug: dkron_completion_fish +url: /cli/dkron_completion_fish/ +--- +## dkron completion fish + +Generate the autocompletion script for fish + +### Synopsis + +Generate the autocompletion script for the fish shell. + +To load completions in your current shell session: + + dkron completion fish | source + +To load completions for every new session, execute once: + + dkron completion fish > ~/.config/fish/completions/dkron.fish + +You will need to start a new shell for this setup to take effect. + + +``` +dkron completion fish [flags] +``` + +### Options + +``` + -h, --help help for fish + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron completion](/docs/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_completion_powershell.md b/website/versioned_docs/version-v3/cli/dkron_completion_powershell.md new file mode 100644 index 000000000..582aaa135 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_completion_powershell.md @@ -0,0 +1,44 @@ +--- +date: 2022-06-05 +title: "dkron completion powershell" +slug: dkron_completion_powershell +url: /cli/dkron_completion_powershell/ +--- +## dkron completion powershell + +Generate the autocompletion script for powershell + +### Synopsis + +Generate the autocompletion script for powershell. + +To load completions in your current shell session: + + dkron completion powershell | Out-String | Invoke-Expression + +To load completions for every new session, add the output of the above command +to your powershell profile. + + +``` +dkron completion powershell [flags] +``` + +### Options + +``` + -h, --help help for powershell + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron completion](/docs/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_completion_zsh.md b/website/versioned_docs/version-v3/cli/dkron_completion_zsh.md new file mode 100644 index 000000000..93250aafd --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_completion_zsh.md @@ -0,0 +1,54 @@ +--- +date: 2022-06-05 +title: "dkron completion zsh" +slug: dkron_completion_zsh +url: /cli/dkron_completion_zsh/ +--- +## dkron completion zsh + +Generate the autocompletion script for zsh + +### Synopsis + +Generate the autocompletion script for the zsh shell. + +If shell completion is not already enabled in your environment you will need +to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + +To load completions for every new session, execute once: + +#### Linux: + + dkron completion zsh > "~/.zsh/completion/_dkron" + +#### macOS: + + dkron completion zsh > /usr/local/share/zsh/site-functions/_dkron + +You will need to start a new shell for this setup to take effect. + + +``` +dkron completion zsh [flags] +``` + +### Options + +``` + -h, --help help for zsh + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron completion](/docs/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_doc.md b/website/versioned_docs/version-v3/cli/dkron_doc.md new file mode 100644 index 000000000..ed5347642 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_doc.md @@ -0,0 +1,40 @@ +--- +date: 2022-06-05 +title: "dkron doc" +slug: dkron_doc +url: /cli/dkron_doc/ +--- +## dkron doc + +Generate Markdown documentation for the Dkron CLI. + +### Synopsis + +Generate Markdown documentation for the Dkron CLI. +This command is, mostly, used to create up-to-date documentation +of Dkron's command-line interface for http://dkron.io/. +It creates one Markdown file per command with front matter suitable +for rendering in Hugo. + +``` +dkron doc [flags] +``` + +### Options + +``` + --dir string the directory to write the doc. (default "/tmp/dkrondoc/") + -h, --help help for doc +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_keygen.md b/website/versioned_docs/version-v3/cli/dkron_keygen.md new file mode 100644 index 000000000..0267f6650 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_keygen.md @@ -0,0 +1,37 @@ +--- +date: 2022-06-05 +title: "dkron keygen" +slug: dkron_keygen +url: /cli/dkron_keygen/ +--- +## dkron keygen + +Generates a new encryption key + +### Synopsis + +Generates a new encryption key that can be used to configure the + agent to encrypt traffic. The output of this command is already + in the proper format that the agent expects. + +``` +dkron keygen [flags] +``` + +### Options + +``` + -h, --help help for keygen +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_leave.md b/website/versioned_docs/version-v3/cli/dkron_leave.md new file mode 100644 index 000000000..0ad11d27f --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_leave.md @@ -0,0 +1,38 @@ +--- +date: 2022-06-05 +title: "dkron leave" +slug: dkron_leave +url: /cli/dkron_leave/ +--- +## dkron leave + +Force an agent to leave the cluster + +### Synopsis + +Stop stops an agent, if the agent is a server and is running for election + stop running for election, if this server was the leader + this will force the cluster to elect a new leader and start a new scheduler. + +``` +dkron leave [flags] +``` + +### Options + +``` + -h, --help help for leave + --rpc-addr string gRPC address of the agent (default "{{ GetPrivateIP }}:6868") +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_raft.md b/website/versioned_docs/version-v3/cli/dkron_raft.md new file mode 100644 index 000000000..1a66ba570 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_raft.md @@ -0,0 +1,30 @@ +--- +date: 2022-06-05 +title: "dkron raft" +slug: dkron_raft +url: /cli/dkron_raft/ +--- +## dkron raft + +Command to perform some raft operations + +### Options + +``` + -h, --help help for raft + --rpc-addr string gRPC address of the agent. (default "{{ GetPrivateIP }}:6868") +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system +* [dkron raft list-peers](/docs/cli/dkron_raft_list-peers/) - Command to list raft peers +* [dkron raft remove-peer](/docs/cli/dkron_raft_remove-peer/) - Command to list raft peers + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_raft_list-peers.md b/website/versioned_docs/version-v3/cli/dkron_raft_list-peers.md new file mode 100644 index 000000000..281b82087 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_raft_list-peers.md @@ -0,0 +1,32 @@ +--- +date: 2022-06-05 +title: "dkron raft list-peers" +slug: dkron_raft_list-peers +url: /cli/dkron_raft_list-peers/ +--- +## dkron raft list-peers + +Command to list raft peers + +``` +dkron raft list-peers [flags] +``` + +### Options + +``` + -h, --help help for list-peers +``` + +### Options inherited from parent commands + +``` + --config string config file path + --rpc-addr string gRPC address of the agent. (default "{{ GetPrivateIP }}:6868") +``` + +### SEE ALSO + +* [dkron raft](/docs/cli/dkron_raft/) - Command to perform some raft operations + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_raft_remove-peer.md b/website/versioned_docs/version-v3/cli/dkron_raft_remove-peer.md new file mode 100644 index 000000000..62d989cf2 --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_raft_remove-peer.md @@ -0,0 +1,33 @@ +--- +date: 2022-06-05 +title: "dkron raft remove-peer" +slug: dkron_raft_remove-peer +url: /cli/dkron_raft_remove-peer/ +--- +## dkron raft remove-peer + +Command to list raft peers + +``` +dkron raft remove-peer [flags] +``` + +### Options + +``` + -h, --help help for remove-peer + --peer-id string Remove a Dkron server with the given ID from the Raft configuration. +``` + +### Options inherited from parent commands + +``` + --config string config file path + --rpc-addr string gRPC address of the agent. (default "{{ GetPrivateIP }}:6868") +``` + +### SEE ALSO + +* [dkron raft](/docs/cli/dkron_raft/) - Command to perform some raft operations + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/cli/dkron_version.md b/website/versioned_docs/version-v3/cli/dkron_version.md new file mode 100644 index 000000000..e8ecc1e0a --- /dev/null +++ b/website/versioned_docs/version-v3/cli/dkron_version.md @@ -0,0 +1,35 @@ +--- +date: 2022-06-05 +title: "dkron version" +slug: dkron_version +url: /cli/dkron_version/ +--- +## dkron version + +Show version + +### Synopsis + +Show the version + +``` +dkron version [flags] +``` + +### Options + +``` + -h, --help help for version +``` + +### Options inherited from parent commands + +``` + --config string config file path +``` + +### SEE ALSO + +* [dkron](/docs/cli/dkron/) - Open source distributed job scheduling system + +###### Auto generated by spf13/cobra on 5-Jun-2022 diff --git a/website/versioned_docs/version-v3/intro.md b/website/versioned_docs/version-v3/intro.md new file mode 100644 index 000000000..a103981a3 --- /dev/null +++ b/website/versioned_docs/version-v3/intro.md @@ -0,0 +1,36 @@ +--- +sidebar_position: 1 +--- +# Intro + +## Dkron - Distributed, fault tolerant job scheduling system + +Welcome to the Dkron documentation! This is the reference guide on how to use Dkron. If you want a getting started guide refer to the [getting started guide](/docs/basics/getting-started). + +## What is Dkron + +Dkron is a distributed system to run scheduled jobs against a server or a group of servers of any size. One of the machines is the leader and the others will be followers. If the leader fails or becomes unreachable, any other one will take over and reschedule all jobs to keep the system healthy. + +In case the old leader becomes alive again, it'll become a follower. + +Dkron is a distributed cron drop-in replacement, easy to setup and fault tolerant with focus in: + +- Easy: Easy to use with a great UI +- Reliable: Completely fault tolerant +- Highly scalable: Able to handle high volumes of scheduled jobs and thousands of nodes + +Dkron is written in Go and leverages the power of distributed key value stores and [Serf](https://www.serfdom.io/) for providing fault tolerance, reliability and scalability while remaining simple and easily installable. + +Dkron is inspired by the google whitepaper [Reliable Cron across the Planet](https://queue.acm.org/detail.cfm?id=2745840) + +Dkron runs on Linux, OSX and Windows. It can be used to run scheduled commands on a server cluster using any combination of servers for each job. It has no single points of failure due to the use of the fault tolerant distributed databases and can work at large scale thanks to the efficient and lightweight gossip protocol. + +Dkron uses the efficient and lightweight [gossip protocol](https://www.serfdom.io/docs/internals/gossip.html) underneath to communicate with nodes. Failure notification and task handling are run efficiently across an entire cluster of any size. + +## Web UI + +![](/img/job-list-new.png) + +## Dkron design + +Dkron is designed to solve one problem well, executing commands in given intervals. Following the unix philosophy of doing one thing and doing it well (like the battle-tested cron) but with the given addition of being designed for the cloud era, removing single points of failure in environments where scheduled jobs are needed to be run in multiple servers. diff --git a/website/versioned_docs/version-v3/pro/_category_.json b/website/versioned_docs/version-v3/pro/_category_.json new file mode 100644 index 000000000..ee73989e7 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Pro" +} diff --git a/website/versioned_docs/version-v3/pro/_index.md b/website/versioned_docs/version-v3/pro/_index.md new file mode 100644 index 000000000..11323c571 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/_index.md @@ -0,0 +1,6 @@ +--- +title: Dkron Pro +weight: 40 +--- + +{{% children style="li" depth="999" %}} diff --git a/website/versioned_docs/version-v3/pro/acls.md b/website/versioned_docs/version-v3/pro/acls.md new file mode 100644 index 000000000..8057be6a4 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/acls.md @@ -0,0 +1,90 @@ +# Access Control (Preview) + +:::info +This feature is in preview and is subject to big changes +::: + +Dkron provides an optional Access Control List (ACL) system which can be used to control access to data and APIs. The ACL is Capability-based, relying on policies to determine which fine grained rules can be applied. Dkron's capability based ACL system is very similar to common ACL systems you are used to. + +## ACL System Overview + +Dkron's ACL system is implemented with the CNCF [Open Policy Agent](https://www.openpolicyagent.org/) bringing a powerful system to suit your needs. + +The ACL system is designed to be easy to use and fast to enforce while providing administrative insight. At the highest level, there are two major components to the ACL system: + +* **OPA policy engine.** OPA provices policy decission making [decoupling](https://www.openpolicyagent.org/docs/latest/philosophy/#policy-decoupling) Dkron integrates OPA as a library and provides a default policy rules written in the OPA Policy language that implements a set of enforcing rules on request params to the API that are ready to use for most use cases. You don not need to learn the OPA Policy language to start using Dkron's ACL system, but you can modify the default policy rules to adapt to your use case if you need to. Read more in [OPA Docs](https://www.openpolicyagent.org/docs/latest/) + +* **ACL Policies.** Dkron's ACL policies are simple JSON documents that define patterns to allow access to resources. You can find below an example ACL policy that works with the default OPA policy. The ACL JSON structure is not rigid you can adapt it to add new features in combination with the OPA Policy rules. + +:::info +This guide is based on the usage of the default OPA Rego Policy +::: + +## Configuring ACLs + +ACLs are not enabled by default and must be enabled. To enable ACLs simply create an ACL policy using the API. Below you can find the most basic example of an ACL policy: + +Basic example policy: +``` +curl localhost:8080/v1/acl/policies -d '{ + "path": { + "/v1": { + "capabilities": [ + "read", + ] + }, + "/v1/**": { + "capabilities": [ + "create", + "read", + "update", + "delete", + "list" + ] + } + } +}' +``` + +This policy allows any request to the API. As you can see paths uses glob patterns, and capabilities allow operations on resources. + +ACLs also allows templating, providing the ability to allow or deny operations to certain resource by patterns without having to hardcode values in policies. + +For example, we can for limit job actions on certain resources based on the provided token via the accepted header `X-Dkron-Token` on the request: + +Example policy: +``` +curl localhost:8080/v1/acl/policies -d '{ + "path": { + "/v1/members": { + "capabilities": ["read"] + }, + "/v1/jobs": { + "capabilities": [ + "list", + "read" + ] + }, + "/v1/jobs/{{.Token}}-*": { + "capabilities": [ + "create", + "read", + "update", + "delete" + ] + } + } +}' +``` + +This policy will allow all operations on jobs starting with `[Token]-job_name`, but will deny manipulation of jobs that doesn't match the pattern. + +## Disable ACLs + +As an administrator you will need to edit policies. Currently to be able to edit ACLs if you get locked out, you need to edit the default Rego file and disable enforcement completely. Edit the file located in `policies/main.rego` and change the `default allow` directive to `true`: + +``` +default allow = false -> true +``` + +This way the policy engine always evaluates to true, allowing any operation again. To restore ACL enforcemen, edit again the `default allow` line and set it back to `false`. diff --git a/website/versioned_docs/version-v3/pro/auth.md b/website/versioned_docs/version-v3/pro/auth.md new file mode 100644 index 000000000..0d946d697 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/auth.md @@ -0,0 +1,13 @@ +# Authentication + +Dkron Pro has the ability to be configured to use HTTP basic auth. + +Authentication can be set using these parameters in the dkron config file: + +```yaml +# dkron.yml +username: dkron_admin +password: adminpassword +``` + +This will enable auth on the WebUI and for the API. diff --git a/website/versioned_docs/version-v3/pro/cli/_category_.json b/website/versioned_docs/version-v3/pro/cli/_category_.json new file mode 100644 index 000000000..e2c485445 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "CLI" +} diff --git a/website/versioned_docs/version-v3/pro/cli/dkron.md b/website/versioned_docs/version-v3/pro/cli/dkron.md new file mode 100644 index 000000000..3e7035016 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron.md @@ -0,0 +1,34 @@ +--- +date: 2023-09-02 +title: "dkron" +slug: dkron +url: /docs/pro/cli/dkron/ +--- +## dkron + +Professional distributed job scheduling system + +### Synopsis + +Dkron is a system service that runs scheduled jobs at given intervals or times, +just like the cron unix service but distributed in several machines in a cluster. +If a machine fails (the leader), a follower will take over and keep running the scheduled jobs without human intervention. + +### Options + +``` + --config string config file (default is /etc/dkron/dkron.yml) + -h, --help help for dkron +``` + +### SEE ALSO + +* [dkron agent](/docs/pro/cli/dkron_agent/) - Start a dkron agent +* [dkron completion](/docs/pro/cli/dkron_completion/) - Generate the autocompletion script for the specified shell +* [dkron doc](/docs/pro/cli/dkron_doc/) - Generate Markdown documentation for the Dkron CLI. +* [dkron keygen](/docs/pro/cli/dkron_keygen/) - Generates a new encryption key +* [dkron leave](/docs/pro/cli/dkron_leave/) - Force an agent to leave the cluster +* [dkron raft](/docs/pro/cli/dkron_raft/) - Command to perform some raft operations +* [dkron version](/docs/pro/cli/dkron_version/) - Show version + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_agent.md b/website/versioned_docs/version-v3/pro/cli/dkron_agent.md new file mode 100644 index 000000000..96d0f1dcc --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_agent.md @@ -0,0 +1,125 @@ +--- +date: 2023-09-02 +title: "dkron agent" +slug: dkron_agent +url: /docs/pro/cli/dkron_agent/ +--- +## dkron agent + +Start a dkron agent + +### Synopsis + +Start a dkron agent that schedule jobs, listen for executions and run executors. +It also runs a web UI. + +``` +dkron agent [flags] +``` + +### Options + +``` + --advertise-addr string Address used to advertise to other nodes in the cluster. By default, + the bind address is advertised. The value supports + go-sockaddr/template format. + --advertise-rpc-port int Use the value of rpc-port by default + --auto-tls Client TLS using generated certificates (default true) + --bind-addr string Specifies which address the agent should bind to for network services, + including the internal gossip protocol and RPC mechanism. This should be + specified in IP format, and can be used to easily bind all network services + to the same address. The value supports go-sockaddr/template format. + (default "{{ GetPrivateIP }}:8946") + --bootstrap-expect int Provides the number of expected servers in the datacenter. Either this value + should not be provided or the value must agree with other servers in the + cluster. When provided, Dkron waits until the specified number of servers are + available and then bootstraps the cluster. This allows an initial leader to be + elected automatically. This flag requires server mode. + --cert-file string Path to the client server TLS cert file + --client-cert-auth Enable client cert authentication + --client-crl-file string Path to the client certificate revocation list file + --cronitor-endpoint string Cronitor endpoint to call for notifications + --data-dir string Specifies the directory to use for server-specific data, including the + replicated log. By default, this is the top-level data-dir, + like [/var/lib/dkron] (default "dkron.data") + --datacenter string Specifies the data center of the local agent. All members of a datacenter + should share a local LAN connection. (default "dc1") + --disable-http-tls Disable TLS for HTTP WebUI/API regardless of TLS configuration + --disable-usage-stats Disable sending anonymous usage stats + --dog-statsd-addr string DataDog Agent address + --dog-statsd-tags strings Datadog tags, specified as key:value + --enable-prometheus Enable serving prometheus metrics + --encrypt string Key for encrypting network traffic. Must be a base64-encoded 16-byte key + --fast Enable fast Raft log. + --federation-mode string Federation mode between clusters in different regions (default "active") + -h, --help help for agent + --http-addr string Address to bind the UI web server to. Only used when server. The value + supports go-sockaddr/template format. (default ":8080") + --join strings An initial agent to join with. This flag can be specified multiple times + --key-file string Path to the client server TLS key file + --log-level string Log level (debug|info|warn|error|fatal|panic) (default "info") + --mail-from string From email address to use + --mail-host string Mail server host address to use for notifications + --mail-password string Mail server password to use + --mail-payload string Notification mail payload + --mail-port uint16 Mail server port + --mail-subject-prefix string Notification mail subject prefix (default "[Dkron]") + --mail-username string Mail server username used for authentication + --node-name string Name of this node. Must be unique in the cluster (default "mariette.local") + --password string authentication password + --pre-webhook-endpoint string Pre-webhook endpoint to call for notifications + --pre-webhook-headers strings Headers to use when calling the pre-webhook. Can be specified multiple times + --pre-webhook-payload string Body of the POST request to send on pre-webhook call + --profile string Profile is used to control the timing profiles used (default "lan") + --raft-duration int RaftDuration An integer indicating the desired duration of the raft fast log (-1 Low, 0 Mid, 1 High) Low no writes at all, Mid (default) fsync every second, High fsync on every change. + --raft-multiplier int An integer multiplier used by servers to scale key Raft timing parameters. + Omitting this value or setting it to 0 uses default timing described below. + Lower values are used to tighten timing and increase sensitivity while higher + values relax timings and reduce sensitivity. Tuning this affects the time it + takes to detect leader failures and to perform leader elections, at the expense + of requiring more network and CPU resources for better performance. By default, + Dkron will use a lower-performance timing that's suitable for minimal Dkron + servers, currently equivalent to setting this to a value of 5 (this default + may be changed in future versions of Dkron, depending if the target minimum + server profile changes). Setting this to a value of 1 will configure Raft to + its highest-performance mode is recommended for production Dkron servers. + The maximum allowed value is 10. (default 1) + --region string Specifies the region the Dkron agent is a member of. A region typically maps + to a geographic region, for example us, with potentially multiple zones, which + map to datacenters such as us-west and us-east (default "global") + --retry-interval string Time to wait between join attempts. (default "30s") + --retry-join strings Address of an agent to join at start time with retries enabled. + Can be specified multiple times. + --retry-max int Maximum number of join attempts. Defaults to 0, which will retry indefinitely. + --rpc-port int RPC Port used to communicate with clients. Only used when server. + The RPC IP Address will be the same as the bind address. (default 6868) + --serf-reconnect-timeout string This is the amount of time to attempt to reconnect to a failed node before + giving up and considering it completely gone. In Kubernetes, you might need + this to about 5s, because there is no reason to try reconnects for default + 24h value. Also Raft behaves oddly if node is not reaped and returned with + same ID, but different IP. + Format there: https://golang.org/pkg/time/#ParseDuration (default "24h") + --server This node is running in server mode + --statsd-addr string Statsd address + --tag strings Tag can be specified multiple times to attach multiple key/value tag pairs + to the given node, specified as key=value + --trusted-ca-file string Path to the client server TLS trusted CA cert file + --ui Enable the web UI on this node. The node must be server. (default true) + --username string authentication username + --webhook-endpoint string Webhook endpoint to call for notifications + --webhook-headers strings Headers to use when calling the webhook URL. Can be specified multiple times + --webhook-payload string Body of the POST request to send on webhook call + --webhook-url string Webhook url to call for notifications. Deprecated, use webhook-endpoint instead +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_completion.md b/website/versioned_docs/version-v3/pro/cli/dkron_completion.md new file mode 100644 index 000000000..e1e4b6774 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_completion.md @@ -0,0 +1,37 @@ +--- +date: 2023-09-02 +title: "dkron completion" +slug: dkron_completion +url: /docs/pro/cli/dkron_completion/ +--- +## dkron completion + +Generate the autocompletion script for the specified shell + +### Synopsis + +Generate the autocompletion script for dkron for the specified shell. +See each sub-command's help for details on how to use the generated script. + + +### Options + +``` + -h, --help help for completion +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system +* [dkron completion bash](/docs/pro/cli/dkron_completion_bash/) - Generate the autocompletion script for bash +* [dkron completion fish](/docs/pro/cli/dkron_completion_fish/) - Generate the autocompletion script for fish +* [dkron completion powershell](/docs/pro/cli/dkron_completion_powershell/) - Generate the autocompletion script for powershell +* [dkron completion zsh](/docs/pro/cli/dkron_completion_zsh/) - Generate the autocompletion script for zsh + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_completion_bash.md b/website/versioned_docs/version-v3/pro/cli/dkron_completion_bash.md new file mode 100644 index 000000000..b53a4fc38 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_completion_bash.md @@ -0,0 +1,58 @@ +--- +date: 2023-09-02 +title: "dkron completion bash" +slug: dkron_completion_bash +url: /docs/pro/cli/dkron_completion_bash/ +--- +## dkron completion bash + +Generate the autocompletion script for bash + +### Synopsis + +Generate the autocompletion script for the bash shell. + +This script depends on the 'bash-completion' package. +If it is not installed already, you can install it via your OS's package manager. + +To load completions in your current shell session: + +``` + source <(dkron completion bash) +``` + +To load completions for every new session, execute once: + +#### Linux: + + dkron completion bash > /etc/bash_completion.d/dkron + +#### macOS: + + dkron completion bash > $(brew --prefix)/etc/bash_completion.d/dkron + +You will need to start a new shell for this setup to take effect. + + +``` +dkron completion bash +``` + +### Options + +``` + -h, --help help for bash + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron completion](/docs/pro/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_completion_fish.md b/website/versioned_docs/version-v3/pro/cli/dkron_completion_fish.md new file mode 100644 index 000000000..9b7df2ec2 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_completion_fish.md @@ -0,0 +1,47 @@ +--- +date: 2023-09-02 +title: "dkron completion fish" +slug: dkron_completion_fish +url: /docs/pro/cli/dkron_completion_fish/ +--- +## dkron completion fish + +Generate the autocompletion script for fish + +### Synopsis + +Generate the autocompletion script for the fish shell. + +To load completions in your current shell session: + + dkron completion fish | source + +To load completions for every new session, execute once: + + dkron completion fish > ~/.config/fish/completions/dkron.fish + +You will need to start a new shell for this setup to take effect. + + +``` +dkron completion fish [flags] +``` + +### Options + +``` + -h, --help help for fish + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron completion](/docs/pro/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_completion_powershell.md b/website/versioned_docs/version-v3/pro/cli/dkron_completion_powershell.md new file mode 100644 index 000000000..8825da5cd --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_completion_powershell.md @@ -0,0 +1,44 @@ +--- +date: 2023-09-02 +title: "dkron completion powershell" +slug: dkron_completion_powershell +url: /docs/pro/cli/dkron_completion_powershell/ +--- +## dkron completion powershell + +Generate the autocompletion script for powershell + +### Synopsis + +Generate the autocompletion script for powershell. + +To load completions in your current shell session: + + dkron completion powershell | Out-String | Invoke-Expression + +To load completions for every new session, add the output of the above command +to your powershell profile. + + +``` +dkron completion powershell [flags] +``` + +### Options + +``` + -h, --help help for powershell + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron completion](/docs/pro/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_completion_zsh.md b/website/versioned_docs/version-v3/pro/cli/dkron_completion_zsh.md new file mode 100644 index 000000000..c9b7312c3 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_completion_zsh.md @@ -0,0 +1,60 @@ +--- +date: 2023-09-02 +title: "dkron completion zsh" +slug: dkron_completion_zsh +url: /docs/pro/cli/dkron_completion_zsh/ +--- +## dkron completion zsh + +Generate the autocompletion script for zsh + +### Synopsis + +Generate the autocompletion script for the zsh shell. + +If shell completion is not already enabled in your environment you will need +to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + +To load completions in your current shell session: + +``` + source <(dkron completion zsh) +``` + +To load completions for every new session, execute once: + +#### Linux: + + dkron completion zsh > "~/.zsh/completion/_dkron" + +#### macOS: + + dkron completion zsh > $(brew --prefix)/share/zsh/site-functions/_dkron + +You will need to start a new shell for this setup to take effect. + + +``` +dkron completion zsh [flags] +``` + +### Options + +``` + -h, --help help for zsh + --no-descriptions disable completion descriptions +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron completion](/docs/pro/cli/dkron_completion/) - Generate the autocompletion script for the specified shell + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_doc.md b/website/versioned_docs/version-v3/pro/cli/dkron_doc.md new file mode 100644 index 000000000..29a9e13c9 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_doc.md @@ -0,0 +1,40 @@ +--- +date: 2023-09-02 +title: "dkron doc" +slug: dkron_doc +url: /docs/pro/cli/dkron_doc/ +--- +## dkron doc + +Generate Markdown documentation for the Dkron CLI. + +### Synopsis + +Generate Markdown documentation for the Dkron CLI. +This command is, mostly, used to create up-to-date documentation +of Dkron's command-line interface for http://dkron.io/. +It creates one Markdown file per command with front matter suitable +for rendering in Hugo. + +``` +dkron doc [flags] +``` + +### Options + +``` + --dir string the directory to write the doc. (default "/tmp/dkrondoc/") + -h, --help help for doc +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_keygen.md b/website/versioned_docs/version-v3/pro/cli/dkron_keygen.md new file mode 100644 index 000000000..9fa2e94e6 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_keygen.md @@ -0,0 +1,37 @@ +--- +date: 2023-09-02 +title: "dkron keygen" +slug: dkron_keygen +url: /docs/pro/cli/dkron_keygen/ +--- +## dkron keygen + +Generates a new encryption key + +### Synopsis + +Generates a new encryption key that can be used to configure the + agent to encrypt traffic. The output of this command is already + in the proper format that the agent expects. + +``` +dkron keygen [flags] +``` + +### Options + +``` + -h, --help help for keygen +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_leave.md b/website/versioned_docs/version-v3/pro/cli/dkron_leave.md new file mode 100644 index 000000000..91db521de --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_leave.md @@ -0,0 +1,44 @@ +--- +date: 2023-09-02 +title: "dkron leave" +slug: dkron_leave +url: /docs/pro/cli/dkron_leave/ +--- +## dkron leave + +Force an agent to leave the cluster + +### Synopsis + +Stop stops an agent, if the agent is a server and is running for election + stop running for election, if this server was the leader + this will force the cluster to elect a new leader and start a new scheduler. + If this is a server and has the scheduler started stop it, ignoring if this server + was participating in leader election or not (local storage). + Then actually leave the cluster. + +``` +dkron leave [flags] +``` + +### Options + +``` + --cert-file string Path to the client server TLS cert file + -h, --help help for leave + --key-file string Path to the client server TLS key file + --rpc-addr string gRPC address of the agent (default "127.0.0.1:6868") + --trusted-ca-file string Path to the client server TLS trusted CA cert file +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_raft.md b/website/versioned_docs/version-v3/pro/cli/dkron_raft.md new file mode 100644 index 000000000..546e0dba8 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_raft.md @@ -0,0 +1,33 @@ +--- +date: 2023-09-02 +title: "dkron raft" +slug: dkron_raft +url: /docs/pro/cli/dkron_raft/ +--- +## dkron raft + +Command to perform some raft operations + +### Options + +``` + --cert-file string Path to the client server TLS cert file + -h, --help help for raft + --key-file string Path to the client server TLS key file + --rpc-addr string gRPC address of the agent. (default "{{ GetPrivateIP }}:6868") + --trusted-ca-file string Path to the client server TLS trusted CA cert file +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system +* [dkron raft list-peers](/docs/pro/cli/dkron_raft_list-peers/) - Command to list raft peers +* [dkron raft remove-peer](/docs/pro/cli/dkron_raft_remove-peer/) - Command to list raft peers + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_raft_list-peers.md b/website/versioned_docs/version-v3/pro/cli/dkron_raft_list-peers.md new file mode 100644 index 000000000..0ab8fbcab --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_raft_list-peers.md @@ -0,0 +1,35 @@ +--- +date: 2023-09-02 +title: "dkron raft list-peers" +slug: dkron_raft_list-peers +url: /docs/pro/cli/dkron_raft_list-peers/ +--- +## dkron raft list-peers + +Command to list raft peers + +``` +dkron raft list-peers [flags] +``` + +### Options + +``` + -h, --help help for list-peers +``` + +### Options inherited from parent commands + +``` + --cert-file string Path to the client server TLS cert file + --config string config file (default is /etc/dkron/dkron.yml) + --key-file string Path to the client server TLS key file + --rpc-addr string gRPC address of the agent. (default "{{ GetPrivateIP }}:6868") + --trusted-ca-file string Path to the client server TLS trusted CA cert file +``` + +### SEE ALSO + +* [dkron raft](/docs/pro/cli/dkron_raft/) - Command to perform some raft operations + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_raft_remove-peer.md b/website/versioned_docs/version-v3/pro/cli/dkron_raft_remove-peer.md new file mode 100644 index 000000000..a0514bd83 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_raft_remove-peer.md @@ -0,0 +1,36 @@ +--- +date: 2023-09-02 +title: "dkron raft remove-peer" +slug: dkron_raft_remove-peer +url: /docs/pro/cli/dkron_raft_remove-peer/ +--- +## dkron raft remove-peer + +Command to list raft peers + +``` +dkron raft remove-peer [flags] +``` + +### Options + +``` + -h, --help help for remove-peer + --peer-id string Remove a Dkron server with the given ID from the Raft configuration. +``` + +### Options inherited from parent commands + +``` + --cert-file string Path to the client server TLS cert file + --config string config file (default is /etc/dkron/dkron.yml) + --key-file string Path to the client server TLS key file + --rpc-addr string gRPC address of the agent. (default "{{ GetPrivateIP }}:6868") + --trusted-ca-file string Path to the client server TLS trusted CA cert file +``` + +### SEE ALSO + +* [dkron raft](/docs/pro/cli/dkron_raft/) - Command to perform some raft operations + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/cli/dkron_version.md b/website/versioned_docs/version-v3/pro/cli/dkron_version.md new file mode 100644 index 000000000..0f940a01b --- /dev/null +++ b/website/versioned_docs/version-v3/pro/cli/dkron_version.md @@ -0,0 +1,35 @@ +--- +date: 2023-09-02 +title: "dkron version" +slug: dkron_version +url: /docs/pro/cli/dkron_version/ +--- +## dkron version + +Show version + +### Synopsis + +Show the version + +``` +dkron version [flags] +``` + +### Options + +``` + -h, --help help for version +``` + +### Options inherited from parent commands + +``` + --config string config file (default is /etc/dkron/dkron.yml) +``` + +### SEE ALSO + +* [dkron](/docs/pro/cli/dkron/) - Professional distributed job scheduling system + +###### Auto generated by spf13/cobra on 2-Sep-2023 diff --git a/website/versioned_docs/version-v3/pro/commercial-faq.md b/website/versioned_docs/version-v3/pro/commercial-faq.md new file mode 100644 index 000000000..01258224d --- /dev/null +++ b/website/versioned_docs/version-v3/pro/commercial-faq.md @@ -0,0 +1,72 @@ +--- +title: Commercial FAQ +--- + +### What is Dkron Pro? + +Dkron Pro is a flavor of Dkron which add more functionality and provide additional support options for customers. + +### Is there a trial version? + +There's no free trial but we do offer a 14 day period with full refund if it does not work for you. + +### Can I get a discount? + +I'm sure you're very nice but no. Everyone pays the same price. + +### What is the license? + +See [COMM-LICENSE](https://github.com/distribworks/dkron/blob/master/COMM-LICENSE). + +### How does Pro licensing work? + +Every organization running Dkron Pro on its own servers must have a license. There's no limit to the number of servers or environments used by that organization. + +### What happens if my subscription lapses? + +You must have an active subscription to run Dkron Pro. After a one week grace period, you'll lose access to the package repository and priority support. You won't get any more updates or bug fixes and `apt-get install dkron-pro` won't work anymore. + +### Can I distribute Dkron Pro to my customers? + +This is a common requirement for "on-site installs" or "appliances" sold to large corporations. + +The standard license is only appropriate for SaaS usage as it does not allow distribution. Dkron Pro have an Appliance license option which **does** allow you to distribute them. The Appliance license is $7,500/yr for Pro. It allows you to distribute the Pro binaries as part of your application and each of your customers to run Dkron Pro **as part of your application only**. Email [support@distrib.works](mailto:support@distrib.works) to purchase. + +### Can you transfer a license? + +Licenses are **not** transferrable to another company. We will transfer the license from a user-specific email to a group email address (e.g. john_smith@example.com -> tech@example.com) but only for **the same domain**. It is strongly recommended that you buy the license using a group email address so the license is not attached to any one employee's email address. + +### What does the license require me to do? + +Your purchase gets you unique access credentials for downloading the Pro packages. The license agreement requires you to keep these access credentials private. If we find your access credentials are ever publicized: + +1. We'll send you a warning email with details. You need to remove the content and send a new email address so we can generate new credentials for you. The old credentials will stop working immediately so you'll need to update your apps. +2. If your credentials are publicized a second time, we reserve the right to permanently remove access (but won't unless it's really egregious - sloppy contractors happen). + +### Can I get a refund? + +Yes, up to two weeks after purchase. Let us know the reason and maybe we can help but either way it's not a problem. Email [support@distrib.works](mailto:support@distrib.works). + +### How do I update my credit card info? + +If you purchased Dkron Pro ([settings](https://gumroad.com/settings)) with a credit card, log into Gumroad with your email address, click the Settings, enter your card info and hit Save. Follow instructions in Gumroad docs https://help.gumroad.com/how-do-i-update-my-credit-card-information I can't provide support for the Gumroad website and don't have the ability to edit customer info - if you can't log in or change your credit card, you can always let your current subscription expire and purchase a new subscription. + +## Can I request a change to the license terms? + +Dkron Pro customers purchasing the Appliance license can ask for changes to the terms and conditions. + +If you don't need the Appliance license but wants to ask for changes to the terms and conditions you will need to purchase a Dkron Pro standard license per production server. + +Email your concerns and we can negotiate something. + +## Can I pay via invoice and purchase order? + +Dkron Pro is credit card only, no exceptions. + +## Contact Info + +Distributed Works + +All billing/support inquiries: support@distrib.works + +Phone: not available diff --git a/website/versioned_docs/version-v3/pro/commercial-support.md b/website/versioned_docs/version-v3/pro/commercial-support.md new file mode 100644 index 000000000..875b884dd --- /dev/null +++ b/website/versioned_docs/version-v3/pro/commercial-support.md @@ -0,0 +1,9 @@ +--- +title: Commercial Support +--- + +Dkron offers only community support. Dkro Pro offers priority support via email. + +## Priority Support + +Covers 1 incident per quarter, with a max response time of 2 working days. Scope is limited to Dkron and Dkron Pro features and APIs, not application integration or infrastructure. For support, email support AT distrib.works. Please email using the same domain as the original license email or explain your connection to the licensed company. diff --git a/website/versioned_docs/version-v3/pro/configuration.md b/website/versioned_docs/version-v3/pro/configuration.md new file mode 100644 index 000000000..fe2fb202f --- /dev/null +++ b/website/versioned_docs/version-v3/pro/configuration.md @@ -0,0 +1,14 @@ +# Configuration + +Dkron Pro uses the [same parameters](/docs/basics/configuration) as Dkron OSS and add some extra parameters. + +### Command line options + +* `--username` - Authentication username +* `--password` - Authentication password +* `--cert-file` - Path to the client server TLS cert file +* `--key-file` - Path to the client server TLS key file +* `--client-crl-file` - Path to the client certificate revocation list file +* `--trusted-ca-file` - Path to the client server TLS trusted CA cert file +* `--client-cert-auth` - Enable client cert authentication +* `--auto-tls` - Client TLS using generated certificates diff --git a/website/versioned_docs/version-v3/pro/encryption.md b/website/versioned_docs/version-v3/pro/encryption.md new file mode 100644 index 000000000..25f01e9a7 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/encryption.md @@ -0,0 +1,25 @@ +# Encryption + +SSL encryption is used for communicating dkron pro nodes. Also if client auth is enabled. This means that no other software running on your local network will be able to talk to dkron servers. + +This ensures that no unexpected usage of the Dkron's store will happen, unless it is another Dkron pro instance. + +SSL encryption is enabled by default in Dkron Pro and can not be disabled, you don't need to do nothing to use it. + +By default Dkron Pro runs with automatically generated SSL certificates, this is enough for using it in a trusted environment but to have a better grade of confidence, it is recommended to run Dkron Pro with custom SSL certificates. + +Follow [this tutorial](https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md) to generate autosigned SSL certificates for your instances. + +:::info +You don't need a client certificate for Dkron server, just add "client auth" usage to your server cert. +::: + +```yaml +# dkron.yaml +auto-tls: false # Set to false to use custom certs +key-file: server-key.pem +cert-file: server.pem +trusted-ca-file: ca.pem +client-cert-auth: true # Enable it to only allow certs signed by the same CA +``` + diff --git a/website/versioned_docs/version-v3/pro/executors/_category_.json b/website/versioned_docs/version-v3/pro/executors/_category_.json new file mode 100644 index 000000000..19f2153f0 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/executors/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Executors" +} diff --git a/website/versioned_docs/version-v3/pro/executors/docker.md b/website/versioned_docs/version-v3/pro/executors/docker.md new file mode 100644 index 000000000..2f8a3566b --- /dev/null +++ b/website/versioned_docs/version-v3/pro/executors/docker.md @@ -0,0 +1,21 @@ +# Docker executor + +Docker executor can launch docker based cron jobs using the docker command of the target node. + +This executor needs a recent version of docker to be available and configured in the target node. + +## Configuration + +To run a docker job create a job config with the docker executor as in this example: + +```json +{ + "executor": "docker", + "executor_config": { + "image": "alpine", //docker image to use + "volumes": "/logs:/var/log/", //comma separated list of volume mappings + "command": "echo \"Hello from dkron\"", //command to pass to run on container + "env": "ENVIRONMENT=variable" //environment variables to pass to the container + } +} +``` diff --git a/website/versioned_docs/version-v3/pro/executors/ecs.md b/website/versioned_docs/version-v3/pro/executors/ecs.md new file mode 100644 index 000000000..c9e5b21c3 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/executors/ecs.md @@ -0,0 +1,82 @@ +# AWS ECS Executor + +The ECS exeutor is capable of launching tasks in ECS clusters, then listen to a stream of CloudWatch Logs and return the output. + +To configure a job to be run in ECS, the executor needs a JSON Task definition template or an already defined task in ECS. + +To allow the ECS Task runner to run tasks, the machine running Dkron needs to have the appropriate permissions configured in AWS IAM: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Stmt1460720941000", + "Effect": "Allow", + "Action": [ + "ecs:RunTask", + "ecs:DescribeTasks", + "ecs:DescribeTaskDefinition", + "logs:FilterLogEvents", + "logs:DescribeLogStreams", + "logs:PutLogEvents" + ], + "Resource": [ + "*" + ] + } + ] +} +``` + +To configure a job to be run with the ECS executor: + +Example using an existing taskdef + +```json +{ + "executor": "ecs", + "executor_config": { + "taskdefName": "mytaskdef-family", + "region": "eu-west-1", + "cluster": "default", + "env": "ENVIRONMENT=variable", + "service": "mycontainer", + "overrides": "echo,\"Hello from dkron\"" + } +} +``` + +Example using a provided taskdef + +```json +{ + "executor": "ecs", + "executor_config": { + "taskdefBody": "{\"containerDefinitions\": [{\"essential\": true,\"image\": \"hello-world\",\"memory\": 100,\"name\": \"hello-world\"}],\"family\": \"helloworld\"}", + "region": "eu-west-1", + "cluster": "default", + "fargate": "yes", + "env": "ENVIRONMENT=variable", + "maxAttempts": "5000" + } +} +``` + +This is the complete list of configuration parameters of the plugin: + +``` +taskdefBody +taskdefName +region +cluster +logGroup +fargate +securityGroup +subnet +env +service +overrides +maxAttempts // Defaults to 2000, will perform a check every 6s * 2000 times waiting a total of 12000s or 3.3h +enableExecuteCommand +``` diff --git a/website/versioned_docs/version-v3/pro/failover.md b/website/versioned_docs/version-v3/pro/failover.md new file mode 100644 index 000000000..f1a3e181a --- /dev/null +++ b/website/versioned_docs/version-v3/pro/failover.md @@ -0,0 +1,7 @@ +# Cross region failover + +:::warning +This feature is experimental and should be handled with care. +::: + +Dkron Pro can run federated in failover mode, this allows to have two clusters running in different regions and configure one of the clusters in an active-passive fashion, doing a failover in case the active cluster dies. diff --git a/website/versioned_docs/version-v3/pro/processors/_category_.json b/website/versioned_docs/version-v3/pro/processors/_category_.json new file mode 100644 index 000000000..ce53d5924 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/processors/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Processors" +} diff --git a/website/versioned_docs/version-v3/pro/processors/elasticsearch.md b/website/versioned_docs/version-v3/pro/processors/elasticsearch.md new file mode 100644 index 000000000..86cec5e63 --- /dev/null +++ b/website/versioned_docs/version-v3/pro/processors/elasticsearch.md @@ -0,0 +1,50 @@ +# Elasticsearch processor + +The Elasticsearch processor can fordward execution logs to an ES cluster. It need an already available Elasticsearch installation that is visible in the same network of the target node. + +The output logs of the job execution will be stored in the indicated ES instace. + +:::info For Dkron Pro < v3.2.3 +::: +## Configuration + +```json +{ + "processors": { + "elasticsearch": { + "url": "http://localhost:9200", //comma separated list of Elasticsearch hosts urls (default: http://localhost:9200) + "index": "dkron_logs", //desired index name (default: dkron_logs) + "forward": "false" //forward logs to the next processor (default: false) + } + } +} +``` + +:::info For Dkron Pro > v3.2.3 +::: + +## Configuration + +For increased security and flexibility, configuration of the ES processor is stored in a file named `dkron-processor-elasticsearch.yml` in the same locations as `dkron.yml`, and should include a list of configurations for Elasticsearch, it can include any number of configurations. + +This is an example including all available parameters: +```yaml +es1: + index: dkron-logs + index_date_format: '2006-01-02' + username: elastic + password: XXXXXXXXXXXXX + url: https://localhost:9200 +``` +And for each job you only need to configure the `config` parameter in the processors configuration: + +```json +{ + "processors": { + "elasticsearch": { + "config": "es1", // configuration to use from the config file + "forward": "false" // forward logs to the next processor (default: false) + } + } +} +``` diff --git a/website/versioned_docs/version-v3/pro/processors/email.md b/website/versioned_docs/version-v3/pro/processors/email.md new file mode 100644 index 000000000..11303de0b --- /dev/null +++ b/website/versioned_docs/version-v3/pro/processors/email.md @@ -0,0 +1,34 @@ +# Email processor + +The Email processor provides flexibility to job email notifications. + +Configuration of the email processor is stored in a file named `dkron-processor-email.yml` in the same locations as `dkron.yml`, and should include a list of providers, it can include any number of providers. + +Example: +```yaml +provider1: + host: smtp.myprovider.com + port: 25 + username: myusername + password: mypassword + from: cron@mycompany.com + subjectPrefix: '[Staging] ' +``` + +Then configure each job with the following options: + +Example: + +```json +{ + "processors": { + "email": { + "provider": "provider1", + "emails": "team@mycompany.com, owner@mycompany.com", + "onSuccess": "true" + } + } +} +``` + +By default the email procesor doesn't send emails on job success, the `onSuccess` parameter, enables it, like in the previous example. diff --git a/website/versioned_docs/version-v3/pro/processors/slack.md b/website/versioned_docs/version-v3/pro/processors/slack.md new file mode 100644 index 000000000..e73384c3d --- /dev/null +++ b/website/versioned_docs/version-v3/pro/processors/slack.md @@ -0,0 +1,32 @@ +# Slack processor + +The Slack processor provides slack notifications with multiple configurations and rich format. + +Configuration of the slack processor is stored in a file named `dkron-processor-slack.yml` in the same locations as `dkron.yml`, and should include a list of teams, it can include any number of teams. + +![](/img/slack.png) + +Example: +```yaml +team1: + webhook_url: https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXX + bot_name: Dkron Production +``` + +Then configure each job with the following options: + +Example: + +```json +{ + "processors": { + "slack": { + "team": "team1", + "channel": "#cron-production", + "onSuccess": "true" + } + } +} +``` + +By default the slack procesor doesn't send notifications on job success, the `onSuccess` parameter, enables it, like in the previous example. diff --git a/website/versioned_docs/version-v3/upgrading/_category_.json b/website/versioned_docs/version-v3/upgrading/_category_.json new file mode 100644 index 000000000..d3992612b --- /dev/null +++ b/website/versioned_docs/version-v3/upgrading/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Upgrading" +} diff --git a/website/versioned_docs/version-v3/upgrading/from_v1_to_v2.md b/website/versioned_docs/version-v3/upgrading/from_v1_to_v2.md new file mode 100644 index 000000000..730e38f83 --- /dev/null +++ b/website/versioned_docs/version-v3/upgrading/from_v1_to_v2.md @@ -0,0 +1,43 @@ +--- +title: Upgrade from v1 to v2 +--- + +Dkron v2 brings lots of changes to the previous version. To successfully upgrade from v1 to v2 you need to take care of certain steps. + +## Storage + +Dkron v2 brings native storage, no external storage engine is needed. This change can be shocking for existing users and should be carefully planned to consider Dkron as a stateful service in contrast of a stateless service as in v1. + +You must plan a backup strategy for the data directories, you can configure it using the `--data-dir` parameter, by default `./dkron.data`. + +This will ensure that you can recover cluster data in case of an unexpected failure. + +## Migrating Jobs + +To migrate jobs from v1 to v2, export jobs from the v1 cluster and import them into v2. + +A basic script to do that can be found [here](https://gist.github.com/pjz/94f4bd81a0897fd64db44593078e2156) + +You can take the opportunity to update your job definitions as explained in the following section: + +## Change job name + +In v2 dkron doesn't accept jobs with invalid naming, adapt your scripts or api calls if necessary to set the job name with valid characters. + +## Changing tags and metadata + +Tags behave different in dkron 2. There are several dkron tags that changed names and have become reserved. The reserved tags are: + +- dc +- region +- role +- version +- server +- bootstrap +- expect +- rpc_addr +- port + +You can not set these tags with the `tags` param. Tags `region` and `dc` can be set using the `region` and `datacenter` params, respectively. + +In Dkron 2, the job filtering API now filters on the `metadata` instead of the `tags` field. Jobs have a new param `metadata` that can be used to set any data to classify jobs. These can then be used to filter results returned by the API. diff --git a/website/versioned_docs/version-v3/upgrading/from_v2_0_to_v2_2.md b/website/versioned_docs/version-v3/upgrading/from_v2_0_to_v2_2.md new file mode 100644 index 000000000..6163d8e93 --- /dev/null +++ b/website/versioned_docs/version-v3/upgrading/from_v2_0_to_v2_2.md @@ -0,0 +1,9 @@ +--- +title: Upgrade from v2.0.x to v2.2.x +--- + +## Migrating Jobs + +To migrate jobs from v2.0.x to v2.2.x, export jobs from the v2.0.x cluster and import them into v2.2.x + +Refer to the [backup&restore upgrade guide](/docs/usage/upgrade#backup--restore) diff --git a/website/versioned_docs/version-v3/usage/_category_.json b/website/versioned_docs/version-v3/usage/_category_.json new file mode 100644 index 000000000..e640b5c8a --- /dev/null +++ b/website/versioned_docs/version-v3/usage/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Usage", + "position": 2 +} diff --git a/website/versioned_docs/version-v3/usage/chaining.md b/website/versioned_docs/version-v3/usage/chaining.md new file mode 100644 index 000000000..5930420a2 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/chaining.md @@ -0,0 +1,31 @@ +# Job chaining + +You can set some jobs to run after other job is executed. To setup a job that will be executed after any other given job, just set the `parent_job` property when saving the new job. + +The dependent job will be executed after the main job finished a successful execution. + +Child jobs schedule property will be ignored if it's present. + +Take into account that parent jobs must be created before any child job. + +Example: + +```json +{ + "name": "job1", + "schedule": "@every 10s", + "executor": "shell", + "executor_config": { + "command": "echo \"Hello from parent\"" + } +} + +{ + "name": "child_job", + "parent_job": "job1", + "executor": "shell", + "executor_config": { + "command": "echo \"Hello from child\"" + } +} +``` diff --git a/website/versioned_docs/version-v3/usage/cloud-auto-join.md b/website/versioned_docs/version-v3/usage/cloud-auto-join.md new file mode 100644 index 000000000..01a55ca9f --- /dev/null +++ b/website/versioned_docs/version-v3/usage/cloud-auto-join.md @@ -0,0 +1,342 @@ +--- +description: |- + Dkron supports automatic cluster joining using cloud metadata on various providers. +--- + +# Cloud Auto-join + +As of Dkron 2.0.0, `retry-join` accepts a unified interface using the +[go-discover](https://github.com/hashicorp/go-discover) library for doing +automatic cluster joining using cloud metadata. To use retry-join with a +supported cloud provider, specify the configuration on the command line or +configuration file as a `key=value key=value ...` string. + +If the values contain spaces, equals, backslashes or double quotes then +they need to be double quoted and the usual escaping rules apply. + +```sh +$ dkron agent --retry-join 'provider=my-cloud config=val config2="some other val" ...' +``` + +or via a configuration file: + +```yml +retry-join": ["provider=my-cloud config=val config2=\"some other val\" ..."] +``` + +The cloud provider-specific configurations are detailed below. This can be +combined with static IP or DNS addresses or even multiple configurations +for different providers. + +In order to use discovery behind a proxy, you will need to set +`HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` environment variables per +[Golang `net/http` library](https://golang.org/pkg/net/http/#ProxyFromEnvironment). + +The following sections give the options specific to each supported cloud +provider. + +### Amazon EC2 + +This returns the first private IP address of all servers in the given +region which have the given `tag_key` and `tag_value`. + +```sh +$ dkron agent --retry-join "provider=aws tag_key=... tag_value=..." +``` + +```yml +retry-join: ["provider=aws tag_key=... tag_value=..."] +``` + +- `provider` (required) - the name of the provider ("aws" in this case). +- `tag_key` (required) - the key of the tag to auto-join on. +- `tag_value` (required) - the value of the tag to auto-join on. +- `region` (optional) - the AWS region to authenticate in. +- `addr_type` (optional) - the type of address to discover: `private_v4`, `public_v4`, `public_v6`. Default is `private_v4`. (>= 1.0) +- `access_key_id` (optional) - the AWS access key for authentication (see below for more information about authenticating). +- `secret_access_key` (optional) - the AWS secret access key for authentication (see below for more information about authenticating). + +#### Authentication & Precedence + +- Static credentials `access_key_id=... secret_access_key=...` +- Environment variables (`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) +- Shared credentials file (`~/.aws/credentials` or the path specified by `AWS_SHARED_CREDENTIALS_FILE`) +- ECS task role metadata (container-specific). +- EC2 instance role metadata. + +The only required IAM permission is `ec2:DescribeInstances`, and it is +recommended that you make a dedicated key used only for auto-joining. If the +region is omitted it will be discovered through the local instance's [EC2 +metadata +endpoint](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html). + +### Microsoft Azure + +This returns the first private IP address of all servers in the given region +which have the given `tag_key` and `tag_value` in the tenant and subscription, or in +the given `resource_group` of a `vm_scale_set` for Virtual Machine Scale Sets. + +```sh +$ dkron agent --retry-join "provider=azure tag_name=... tag_value=... tenant_id=... client_id=... subscription_id=... secret_access_key=..." +``` + +```yml +retry-join: ["provider=azure tag_name=... tag_value=... tenant_id=... client_id=... subscription_id=... secret_access_key=..."] +``` + +- `provider` (required) - the name of the provider ("azure" in this case). +- `tenant_id` (required) - the tenant to join machines in. +- `client_id` (required) - the client to authenticate with. +- `secret_access_key` (required) - the secret client key. **NOTE** This value often may have an equals sign in it's value, especially if generated from the Azure Portal, so is important to wrap in single quotes eg. `secret_acccess_key='fpOfcHQJAQBczjAxiVpeyLmX1M0M0KPBST+GU2GvEN4='` + +Variables can also be provided by environmental variables: + +* `ARM_SUBSCRIPTION_ID` for subscription +* `ARM_TENANT_ID` for tenant +* `ARM_CLIENT_ID` for client +* `ARM_CLIENT_SECRET` for secret access key + +Use these configuration parameters when using tags: + +- `tag_name` - the name of the tag to auto-join on. +- `tag_value` - the value of the tag to auto-join on. + +Use these configuration parameters (instead of `tag_name` and `tag_value`) when using Virtual Machine Scale Sets (Dkron 1.0.3 and later): + +- `resource_group` - the name of the resource group to filter on. +- `vm_scale_set` - the name of the virtual machine scale set to filter on. + +When using tags the only permission needed is `Microsoft.Network/networkInterfaces`. + +When using Virtual Machine Scale Sets the only role action needed is `Microsoft.Compute/virtualMachineScaleSets/*/read`. + +### Google Compute Engine + +This returns the first private IP address of all servers in the given +project which have the given `tag_value`. + +```sh +$ dkron agent --retry-join "provider=gce project_name=... tag_value=..." +``` + +```yml +retry_join: ["provider=gce project_name=... tag_value=..."] +``` + +- `provider` (required) - the name of the provider ("gce" in this case). +- `tag_value` (required) - the value of the tag to auto-join on. +- `project_name` (optional) - the name of the project to auto-join on. Discovered if not set. +- `zone_pattern` (optional) - the list of zones can be restricted through an RE2 compatible regular expression. If omitted, servers in all zones are returned. +- `credentials_file` (optional) - the credentials file for authentication. Note, if you set `-config-dir` do not store the credentials.json file in the configuration directory as it will be parsed as a config file and Dkron will fail to start. See below for more information. + +#### Authentication & Precedence + +- Use credentials from `credentials_file`, if provided. +- Use JSON file from `GOOGLE_APPLICATION_CREDENTIALS` environment variable. +- Use JSON file in a location known to the gcloud command-line tool. + - On Windows, this is `%APPDATA%/gcloud/application_default_credentials.json`. + - On other systems, `$HOME/.config/gcloud/application_default_credentials.json`. +- On Google Compute Engine, use credentials from the metadata + server. In this final case any provided scopes are ignored. + +Discovery requires a [GCE Service +Account](https://cloud.google.com/compute/docs/access/service-accounts). +Credentials are searched using the following paths, in order of precedence. + +### IBM SoftLayer + +This returns the first private IP address of all servers for the given +datacenter with the given `tag_value`. + +```sh +$ dkron agent --retry-join "provider=softlayer datacenter=... tag_value=... username=... api_key=..." +``` + +```yml +retry-join: ["provider=softlayer datacenter=... tag_value=... username=... api_key=..."] +``` + +- `provider` (required) - the name of the provider ("softlayer" in this case). +- datacenter (required) - the name of the datacenter to auto-join in. +- `tag_value` (required) - the value of the tag to auto-join on. +- `username` (required) - the username to use for auth. +- `api_key` (required) - the api key to use for auth. + +### Aliyun (Alibaba Cloud) + +This returns the first private IP address of all servers for the given +`region` with the given `tag_key` and `tag_value`. + +```sh +$ dkron agent --retry-join "provider=aliyun region=... tag_key=dkron tag_value=... access_key_id=... access_key_secret=..." +``` + +```yml +retry-join: ["provider=aliyun region=... tag_key=dkron tag_value=... access_key_id=... access_key_secret=..."] +``` + +- `provider` (required) - the name of the provider ("aliyun" in this case). +- `region` (required) - the name of the region. +- `tag_key` (required) - the key of the tag to auto-join on. +- `tag_value` (required) - the value of the tag to auto-join on. +- `access_key_id` (required) -the access key to use for auth. +- `access_key_secret` (required) - the secret key to use for auth. + +The required RAM permission is `ecs:DescribeInstances`. +It is recommended you make a dedicated key used only for auto-joining. + +### Digital Ocean + +This returns the first private IP address of all servers for the given +`region` with the given `tag_name`. + +```sh +$ dkron agent --retry-join "provider=digitalocean region=... tag_name=... api_token=..." +``` + +```yml +retry-join: ["provider=digitalocean region=... tag_name=... api_token=..."] +``` + +- `provider` (required) - the name of the provider ("digitalocean" in this case). +- `region` (required) - the name of the region. +- `tag_name` (required) - the value of the tag to auto-join on. +- `api_token` (required) -the token to use for auth. + +### Openstack + +This returns the first private IP address of all servers for the given +`region` with the given `tag_key` and `tag_value`. + +```sh +$ dkron agent --retry-join "provider=os tag_key=dkron tag_value=server username=... password=... auth_url=..." +``` + +```yml +retry-join: ["provider=os tag_key=dkron tag_value=server username=... password=... auth_url=..."] +``` + +- `provider` (required) - the name of the provider ("os" in this case). +- `tag_key` (required) - the key of the tag to auto-join on. +- `tag_value` (required) - the value of the tag to auto-join on. +- `project_id` (optional) - the id of the project (tenant id). +- `username` (optional) - the username to use for auth. +- `password` (optional) - the password to use for auth. +- `token` (optional) - the token to use for auth. +- `auth_url` (optional) - the identity endpoint to use for auth. +- `insecure` (optional) - indicates whether the API certificate should not be checked. Any value means `true`. + +The configuration can also be provided by environment variables. + +### Scaleway + +This returns the first private IP address of all servers for the given +`region` with the given `tag_name`. + +```sh +$ dkron agent --retry-join "provider=scaleway organization=my-org tag_name=dkron-server token=... region=..." +``` + +```yml +retry-join: ["provider=scaleway organization=my-org tag_name=dkron-server token=... region=..."] +``` + +- `provider` (required) - the name of the provider ("scaleway" in this case). +- `region` (required) - the name of the region. +- `tag_name` (required) - the name of the tag to auto-join on. +- `organization` (required) - the organization access key to use for auth (equal to access key). +- `token` (required) - the token to use for auth. + +### Joyent Triton + +This returns the first PrimaryIP addresses for all servers with the given `tag_key` and `tag_value`. + +```sh +$ dkron agent --retry-join "provider=triton account=testaccount url=https://us-sw-1.api.joyentcloud.com key_id=... tag_key=dkron-role tag_value=server" +``` + +```yml +retry-join: ["provider=triton account=testaccount url=https://us-sw-1.api.joyentcloud.com key_id=... tag_key=dkron-role tag_value=server"] +``` + +- `provider` (required) - the name of the provider ("triton" in this case). +- `account` (required) - the name of the account. +- `url` (required) - the URL of the Triton api endpoint to use. +- `key_id` (required) - the key id to use. +- `tag_key` (optional) - the instance tag key to use. +- `tag_value` (optional) - the tag value to use. + + +### vSphere + +This returns the first private IP address of all servers for the given region with the given `tag_name` and `category_name`. + +```sh +$ dkron agent --retry-join "provider=vsphere category_name=dkron-role tag_name=dkron-server host=... user=... password=... insecure_ssl=[true|false]" +``` + +```yml +retry-join: ["provider=vsphere category_name=dkron-role tag_name=dkron-server host=... user=... password=... insecure_ssl=[true|false]"] +``` + +- `provider` (required) - the name of the provider ("vsphere" is the provider here) +- `tag_name` (required) - The name of the tag to look up. +- `category_name` (required) - The category of the tag to look up. +- `host` (required) - The host of the vSphere server to connect to. +- `user` (required) - The username to connect as. +- `password` (required) - The password of the user to connect to vSphere as. +- `insecure_ssl` (optional) - Whether or not to skip SSL certificate validation. +- `timeout` (optional) - Discovery context timeout (default: 10m) + +### Packet + +This returns the first private IP address (or the IP address of `address type`) of all servers with the given `project` and `auth_token`. + +```sh +$ dkron agent --retry-join "provider=packet auth_token=token project=uuid url=... address_type=..." +``` + +```yml +retry-join: ["provider=packet auth_token=token project=uuid url=... address_type=..."] +``` + +- `provider` (required) - the name of the provider ("packet" is the provider here) +- `project` (required) - the UUID of packet project +- `auth_token` (required) - the authentication token for packet +- `url` (optional) - a REST URL for packet +- `address_type` (optional) - the type of address to check for in this provider ("private_v4", "public_v4" or "public_v6". Defaults to "private_v4") + + +### Kubernetes (k8s) + +The Kubernetes provider finds the IP addresses of pods with the matching +[label or field selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/). +This is useful for non-Kubernetes agents that are joining a server cluster +running within Kubernetes. + +The pod IP is used by default, which requires that the agent connecting can +network to the pod IP. The `host_network` boolean can be set to true to use +the host IP instead, but this requires the agent ports (Gossip, RPC, etc.) +to be exported to the host as well. + +By default, no port is specified. This causes Dkron to use the default +gossip port (default behavior with all join requests). The pod may specify +the `dkron.hashicorp.com/auto-join-port` annotation to set the port. The value +may be an integer or a named port. + +```sh +$ dkron agent --retry-join "provider=k8s label_selector=\"app=dkron,component=server\"" +``` + +```yml +retry-join: ["provider=k8s label_selector=..."] +``` + +- `provider` (required) - the name of the provider ("k8s" is the provider here) +- `kubeconfig` (optional) - path to the kubeconfig file. If this isn't + set, then in-cluster auth will be attempted. If that fails, the default + kubeconfig paths are tried (`$HOME/.kube/config`). +- `namespace` (optional) - the namespace to search for pods. If this isn't + set, it defaults to all namespaces. +- `label_selector` (optional) - the label selector for matching pods. +- `field_selector` (optional) - the field selector for matching pods. diff --git a/website/versioned_docs/version-v3/usage/clustering.md b/website/versioned_docs/version-v3/usage/clustering.md new file mode 100644 index 000000000..2da5b6015 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/clustering.md @@ -0,0 +1,89 @@ +--- +title: Clustering +--- + +## Configure a cluster + +Dkron can run in HA mode, avoiding SPOFs, this mode provides better scalability and better reliability for users that wants a high level of confidence in the cron jobs they need to run. + +Manually bootstrapping a Dkron cluster does not rely on additional tooling, but does require operator participation in the cluster formation process. When bootstrapping, Dkron servers and clients must be started and informed with the address of at least one Dkron server. + +As you can tell, this creates a chicken-and-egg problem where one server must first be fully bootstrapped and configured before the remaining servers and clients can join the cluster. This requirement can add additional provisioning time as well as ordered dependencies during provisioning. + +First, we bootstrap a single Dkron server and capture its IP address. After we have that nodes IP address, we place this address in the configuration. + +1. First bootstrap a node with a configuration like this: + +```yaml +# dkron.yml +server: true +bootstrap-expect: 1 +``` + +2. Then stop the bootstrapped server and capture the server IP address. + +3. To form a cluster, configure server nodes (including the bootstrapped server) with the address of its peers as in the following example: + +```yaml +# dkron.yml +server: true +bootstrap-expect: 3 +retry-join: +- 10.19.3.9 +- 10.19.4.64 +- 10.19.7.215 +``` + +## Deployment Table + +Below is a table that shows quorum size and failure tolerance for various +cluster sizes. The recommended deployment is either 3 or 5 servers. A single +server deployment is _**highly**_ discouraged as data loss is inevitable in a +failure scenario. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ServersQuorum SizeFailure Tolerance
110
220
321
431
532
642
743
diff --git a/website/versioned_docs/version-v3/usage/concurrency.md b/website/versioned_docs/version-v3/usage/concurrency.md new file mode 100644 index 000000000..90cacf516 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/concurrency.md @@ -0,0 +1,27 @@ +--- +title: Concurrency +toc: true +--- + +## Concurrency + +Jobs can be configured to allow overlapping executions or forbid them. + +Concurrency property accepts two option: + +* **allow** (default): Allow concurrent job executions. +* **forbid**: If the job is already running don't send the execution, it will skip the executions until the next schedule. + +Example: + +```json +{ + "name": "job1", + "schedule": "@every 10s", + "executor": "shell", + "executor_config": { + "command": "echo \"Hello from parent\"" + }, + "concurrency": "forbid" +} +``` diff --git a/website/versioned_docs/version-v3/usage/cron-spec.md b/website/versioned_docs/version-v3/usage/cron-spec.md new file mode 100644 index 000000000..77baf8025 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/cron-spec.md @@ -0,0 +1,114 @@ +--- +title: Cron spec +weight: 20 +--- + +## CRON Expression Format + +A cron expression represents a set of times, using 6 space-separated fields. + + Field name | Mandatory? | Allowed values | Allowed special characters + ---------- | ---------- | -------------- | -------------------------- + Seconds | Yes | 0-59 | * / , - + Minutes | Yes | 0-59 | * / , - + Hours | Yes | 0-23 | * / , - + Day of month | Yes | 1-31 | * / , - ? + Month | Yes | 1-12 or JAN-DEC | * / , - + Day of week | Yes | 0-6 or SUN-SAT | * / , - ? + +Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun", +and "sun" are equally accepted. + +Special Characters + +Asterisk ( * ) + +The asterisk indicates that the cron expression will match for all values of the +field; e.g., using an asterisk in the 5th field (month) would indicate every +month. + +Slash ( / ) + +Slashes are used to describe increments of ranges. For example 3-59/15 in the +1st field (seconds) would indicate the 3rd second of the minute and every 15 +seconds thereafter. The form "*\/..." is equivalent to the form "first-last/...", +that is, an increment over the largest possible range of the field. The form +"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the +increment until the end of that specific range. It does not wrap around. + +Comma ( , ) + +Commas are used to separate items of a list. For example, using "MON,WED,FRI" in +the 6th field (day of week) would mean Mondays, Wednesdays and Fridays. + +Hyphen ( - ) + +Hyphens are used to define ranges. For example, 9-17 would indicate every +hour between 9am and 5pm inclusive. + +Question mark ( ? ) + +Question mark may be used instead of '*' for leaving either day-of-month or +day-of-week blank. + +### Predefined schedules + +You may use one of several pre-defined schedules in place of a cron expression. + + Entry | Description | Equivalent To + ----- | ----------- | ------------- + @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * + @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * + @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 + @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * + @hourly | Run once an hour, beginning of hour | 0 0 * * * * + @minutely | Run once a minute, beginning of minute | 0 * * * * * + @manually | Never runs | N/A + +### Intervals + +You may also schedule a job to execute at fixed intervals. This is supported by +formatting the cron spec like this: + +``` + @every +``` + +where "duration" is a string accepted by time.ParseDuration http://golang.org/pkg/time/#ParseDuration. + +For example, "@every 1h30m10s" would indicate a schedule that activates every +1 hour, 30 minutes, 10 seconds. + +Note: The interval does not take the job runtime into account. For example, +if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, +it will have only 2 minutes of idle time between each run. + +### Fixed times + +You may also want to schedule a job to be executed once. This is supported by +formatting the cron spec like this: + +``` + @at +``` + +Where "datetime" is a string accepted by time.Parse in RFC3339 format https://golang.org/pkg/time/#Parse. + +For example, "@at 2018-01-02T15:04:00Z" would run the job on the specified date and time +assuming UTC timezone. + +### Time zones + +Dkron is able to schedule jobs in time zones, if you specify the `timezone` parameter in a +job definition. + +If the time zone is not specified, the following rules apply: + +All interpretation and scheduling is done in the machine's local time zone (as +provided by the Go time package http://www.golang.org/pkg/time. + +Be aware that jobs scheduled during daylight-savings leap-ahead transitions will +not be run! + +If you specify `timezone` the job will be scheduled taking into account daylight-savings +and leap-ahead transitions, running the job in the actual time in the specified time zone. diff --git a/website/versioned_docs/version-v3/usage/cronitor.md b/website/versioned_docs/version-v3/usage/cronitor.md new file mode 100644 index 000000000..578c37cc8 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/cronitor.md @@ -0,0 +1,29 @@ +# Cronitor Integration + +:::info +This feature is available since v3.2.0 +::: + +Dkron includes tight integration with [Cronitor](https://cronitor.io/) for advanced execution monitoring, check their product page for more information. + +To setup the integration add this parameter to your `dkron.yaml` config file, including your private cronitor API endpoint key: + +:::caution +Remember: do not check your config file into your source code repository, it contains sensitive information. +::: + +``` +cronitor-endpoint: https://cronitor.link/p/xxxxxxxxxxxxxxxxxxxxxx +``` + +You can also use the `--cronitor-endpoint` CLI parameter or the `DKRON_CRONITOR_ENDPOINT` env variable. + +Dkron will call Cronitor before and after running a job, sending all necessary information, no further configuration is necessary as Cronitor automagically create a monitor for each job. + +![](/img/cronitor1.jpg) + +You can further configure your job using the Cronitor UI or API after it has run at least once. + +Cronitor can be used to notify over different channels using integrations, go to the settings page to integrate it with several popular services: + +![](/img/cronitor2.jpg) diff --git a/website/versioned_docs/version-v3/usage/ecs.md b/website/versioned_docs/version-v3/usage/ecs.md new file mode 100644 index 000000000..8101ed926 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/ecs.md @@ -0,0 +1,26 @@ +--- +title: Use with AWS ECS +--- + +:::tip +[Dkron Pro](/pro) comes with a [native ECS executor](/docs/pro/executors/ecs/) out of the box. +::: + +## Use with Amazon ECS + +To use Dkron to schedule jobs that run in containers, a wrapper ECS script is needed. + +Install the following snippet in the node that will run the call to ECS + + + +### Prerequisites + +The node that will run the call to ECS will need to have installed + +* AWS cli +* jq + +### Example + +`ecs-run --cluster cron --task-definition cron-taskdef --container-name cron --region us-east-1 --command "rake foo"` diff --git a/website/versioned_docs/version-v3/usage/executors/grpc.md b/website/versioned_docs/version-v3/usage/executors/grpc.md new file mode 100644 index 000000000..d096fb421 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/executors/grpc.md @@ -0,0 +1,33 @@ +# GRPC Executor + +GRPC executor can send a request to a GRPC Server + + +## Requirements +In order to serialize protobufs the server need's to have the reflection service active. +Without that we cannot get proto descriptors required for serialization. + +## Configuration + +Params: + +``` +"url": Required, Request url +"body": Optional, POST body +"timeout": Optional, Request timeout, unit seconds +"expectCode": Optional, One of https://grpc.github.io/grpc/core/md_doc_statuscodes.html +``` + +Example + +```json +{ + "executor": "http", + "executor_config": { + "url": "127.0.0.1:9000/test.TestService/Test", + "body": "{\"key\": \"value\"}", + "timeout": "30", + "expectCode": "0" + } +} +``` diff --git a/website/versioned_docs/version-v3/usage/executors/http.md b/website/versioned_docs/version-v3/usage/executors/http.md new file mode 100644 index 000000000..36ab3c1a7 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/executors/http.md @@ -0,0 +1,40 @@ +# HTTP Executor + +HTTP executor can send a request to an HTTP endpoint + +## Configuration + +Params: + +``` +method: Request method in uppercase +url: Request url +headers: Json string, such as "[\"Content-Type: application/json\"]" +body: POST body +timeout: Request timeout, unit seconds +expectCode: Expect response code, such as 200,206 +expectBody: Expect response body, support regexp, such as /success/ +debug: Debug option, will log everything when this option is not empty +tlsNoVerifyPeer: false (default) or true. If true, disables verification of the remote SSL certificate's validity. +tlsCertificateFile: Path to the PEM file containing the client certificate. Optional. +tlsCertificateKeyFile: Path to the PEM file containing the client certificate private key. Optional. +tlsRootCAsFile: Path to the PEM file containing certificates to use as root CAs. Optional. +``` + +Example + +```json +{ + "executor": "http", + "executor_config": { + "method": "GET", + "url": "http://example.com", + "headers": "[]", + "body": "", + "timeout": "30", + "expectCode": "200", + "expectBody": "", + "debug": "true" + } +} +``` diff --git a/website/versioned_docs/version-v3/usage/executors/index.md b/website/versioned_docs/version-v3/usage/executors/index.md new file mode 100644 index 000000000..831945e26 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/executors/index.md @@ -0,0 +1,9 @@ +# Executors + +Executor plugins are the main mechanism of execution in Dkron. They implement different "types" of jobs in the sense that they can perform the most diverse actions on the target nodes. + +For example, the built-in `shell` executor, will run the indicated command on the target node. + +New plugins will be added, or you can create new ones, to perform different tasks, such as HTTP requests, Docker runs, anything that you can imagine. + +If you need more features you can check [Dkron Pro](/pro) that brings commercially supported plugins. diff --git a/website/versioned_docs/version-v3/usage/executors/kafka.md b/website/versioned_docs/version-v3/usage/executors/kafka.md new file mode 100644 index 000000000..a4eda3f77 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/executors/kafka.md @@ -0,0 +1,29 @@ +# Kafka Executor + +A basic Kafka executor that produces a message on a Kafka broker. + +## Configuration + +Params + +``` +brokerAddress: Comma separated string containing "IP:port" of the brokers +key: The key of the message to produce +message: The body of the message to produce +topic: The Kafka topic for this message +tlsEnable: Enables TLS if set to true. Optional +tlsInsecureSkipVerify: Disables verification of the remote SSL certificate's validity if set to true. Optional +debug: Turns on debugging output if not empty +``` + +Example + +```json +"executor": "kafka", +"executor_config": { + "brokerAddress": "localhost:9092,another.host:9092", + "key": "My key", + "message": "My message", + "topic": "my_topic" +} +``` diff --git a/website/versioned_docs/version-v3/usage/executors/nats.md b/website/versioned_docs/version-v3/usage/executors/nats.md new file mode 100644 index 000000000..d68c281a0 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/executors/nats.md @@ -0,0 +1,32 @@ +# NATS Executor + +The NATS executor sends a message to a NATS server/cluster. + +Currently, only username/password authentication is supported. + +## Configuration + +Params + +``` +url: Comma separated list of NATS server URLs +message: The message to send +subject: The subject to send the message to +userName: username for authentication +password: password for authentication +debug: If not empty, turns on debugging. Will log the NATS specific job config and the request sent. +``` + +Example + +```json +{ + "executor": "nats", + "executor_config": { + "url": "tls://nats.demo.io:4443", + "message": "the message", + "subject": "myfavoritesubject", + "userName":"someusername", + "password":"somepassword" +} +``` diff --git a/website/versioned_docs/version-v3/usage/executors/shell.md b/website/versioned_docs/version-v3/usage/executors/shell.md new file mode 100644 index 000000000..bdd973510 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/executors/shell.md @@ -0,0 +1,42 @@ +# Shell Executor + +Shell executor runs a system command + +## Configuration + +Params + +``` +shell: Run this command using a shell environment +command: The command to run +env: Env vars separated by comma +cwd: Chdir before command run +timeout: Force kill job after specified time. Format: https://golang.org/pkg/time/#ParseDuration. +``` + +Example + +```json +{ + "executor": "shell", + "executor_config": { + "shell": "true", + "command": "my_command", + "env": "ENV_VAR=va1,ANOTHER_ENV_VAR=var2", + "cwd": "/app", + "timeout": "24h" + } +} +``` + +## Job execution prometheus metrics +Path: `/metrics` +Port: 9422 +or configure via environment variable `SHELL_EXECUTOR_PROMETHEUS_PORT` + +### Exposed metrics + +| Name | Type | Description | Labels | +|------------------------|:------|-------------------------------:|---------:| +| dkron_job_cpu_usage | gauge | current CPU usage by job | job_name | +| dkron_job_mem_usage_kb | gauge | current memory consumed by job | job_name | diff --git a/website/versioned_docs/version-v3/usage/metatags.md b/website/versioned_docs/version-v3/usage/metatags.md new file mode 100644 index 000000000..8604d3474 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/metatags.md @@ -0,0 +1,24 @@ +--- +title: Job metadata +--- + +## Job metadata + +Jobs can have an optional extra property field called `metadata` that allows to set arbitrary tags to jobs and query the jobs using the API: + +```json +{ + "name": "job_name", + "command": "/bin/true", + "schedule": "@every 2m", + "metadata": { + "user_id": "12345" + } +} +``` + +And then query the API to get only the results needed: + +``` +$ curl http://localhost:8080/v1/jobs --data-urlencode "metadata[user_id]=12345"` +``` diff --git a/website/versioned_docs/version-v3/usage/metrics.md b/website/versioned_docs/version-v3/usage/metrics.md new file mode 100644 index 000000000..efd0aced5 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/metrics.md @@ -0,0 +1,73 @@ +--- +title: Metrics +--- + +Dkron has the ability to send metrics to Statsd for dashboards and historical reporting or provide prometheus format metrics via the api. It sends job processing metrics, golang, and serf metrics. + +## Configuration + +### Statsd + +Add this in your yaml config file to enable statsd metrics. + +```yaml +statsd-addr: "localhost:8125" +# Or for datadog statsd +dog-statsd-addr: "localhost:8125" +``` + +### Prometheus + +Add this to your yaml config file to enable serving prometheus metrics at the endpoint `/metrics` + +```yaml +enable-prometheus: true +``` + +Additionally, in your Prometheus config file (prometheus.yml), add the following to link dkron metric endpoint +```yaml +scrape_configs: + ... #initial configuration + + - job_name: "dkron_metrics" + # metrics_path defaults to '/metrics' + static_configs: + - targets: ["localhost:6080"] +``` + +## Metrics + +- dkron.agent.event_received.query_execution_done +- dkron.agent.event_received.query_run_job +- dkron.memberlist.gossip +- dkron.memberlist.probeNode +- dkron.memberlist.pushPullNode +- dkron.memberlist.tcp.accept +- dkron.memberlist.tcp.connect +- dkron.memberlist.tcp.sent +- dkron.memberlist.udp.received +- dkron.memberlist.udp.sent +- dkron.grpc.call_execution_done +- dkron.grpc.call_get_job +- dkron.grpc.execution_done +- dkron.grpc.get_job +- dkron.runtime.alloc_bytes +- dkron.runtime.free_count +- dkron.runtime.gc_pause_ns +- dkron.runtime.heap_objects +- dkron.runtime.malloc_count +- dkron.runtime.num_goroutines +- dkron.runtime.sys_bytes +- dkron.runtime.total_gc_pause_ns +- dkron.runtime.total_gc_runs +- dkron.serf.coordinate.adjustment_ms +- dkron.serf.msgs.received +- dkron.serf.msgs.sent +- dkron.serf.queries +- dkron.serf.queries.execution_done +- dkron.serf.queries.run_job +- dkron.serf.query_acks +- dkron.serf.query_responses +- dkron.serf.queue.Event +- dkron.serf.queue.Intent +- dkron.serf.queue.Query diff --git a/website/versioned_docs/version-v3/usage/plugins/_category_.json b/website/versioned_docs/version-v3/usage/plugins/_category_.json new file mode 100644 index 000000000..732c367e0 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/plugins/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Plugins" +} diff --git a/website/versioned_docs/version-v3/usage/plugins/develop.md b/website/versioned_docs/version-v3/usage/plugins/develop.md new file mode 100644 index 000000000..db3e9a3c1 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/plugins/develop.md @@ -0,0 +1,36 @@ +--- +title: Developing plugins +weight: 99 +--- + +## Developing a Plugin + +:::warning Advanced topic! +Plugin development is a highly advanced topic, and is not required knowledge for day-to-day usage. If you don't plan on writing any plugins, we recommend not reading the following section of the documentation. +::: + +Developing a plugin is simple. The only knowledge necessary to write a plugin is basic command-line skills and basic knowledge of the Go programming language. + +Note: A common pitfall is not properly setting up a `$GOPATH`. This can lead to strange errors. You can read more about this here to familiarize yourself. + +Create a new Go project somewhere in your `$GOPATH`. If you're a GitHub user, we recommend creating the project in the directory `$GOPATH/src/github.com/USERNAME/dkron-NAME-TYPE`, where `USERNAME` is your GitHub username and `NAME` is the name of the plugin you're developing. This structure is what Go expects and simplifies things down the road. + +With the directory made, create a main.go file. This project will be a binary so the package is "main": + +```go +package main + +import ( + "github.com/distribworks/dkron/v3/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + Processor: new(MyPlugin), + }) +} +``` + +And that's basically it! You'll have to change the argument given to plugin.Serve to be your actual plugin, but that is the only change you'll have to make. The argument should be a structure implementing one of the plugin interfaces (depending on what sort of plugin you're creating). + +Dkron plugins must follow a very specific naming convention of `dkron-TYPE-NAME`. For example, `dkron-processor-files`, which tells Dkron that the plugin is a processor that can be referenced as "files". diff --git a/website/versioned_docs/version-v3/usage/plugins/index.md b/website/versioned_docs/version-v3/usage/plugins/index.md new file mode 100644 index 000000000..b5f1e01c4 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/plugins/index.md @@ -0,0 +1,20 @@ +# Plugins + +## Intro + +Plugins in Dkron allow you to add funcionality that integrates with the workflow of the job execution in Dkron. It's a powerful system that allows you to extend and adapt Dkron to your special needs. + +This page documents the basics of how the plugin system in Dkron works, and how to setup a basic development environment for plugin development if you're writing a Dkron plugin. + +## How it Works + +Dkron execution execution processors are provided via plugins. Each plugin exposes functionality for modifying the execution. Plugins are executed as a separate process and communicate with the main Dkron binary over an RPC interface. + +The code within the binaries must adhere to certain interfaces. The network communication and RPC is handled automatically by higher-level libraries. The exact interface to implement is documented in its respective documentation section. + +## Installing a Plugin + +Dkron searches for plugins at startup, to install a plugin just drop the binary in one of the following locations: + +1. /etc/dkron/plugins +2. Dkron executable directory diff --git a/website/versioned_docs/version-v3/usage/processors/file.md b/website/versioned_docs/version-v3/usage/processors/file.md new file mode 100644 index 000000000..feb758dd9 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/processors/file.md @@ -0,0 +1,33 @@ +--- +title: File Processor +--- + +File processor saves the execution output to a single log file in the specified directory + +## Configuration + +Parameters + +``` +log_dir: Path to the location where the log files will be saved +forward: Forward log output to the next processor +``` + +Example + +```json +{ + "name": "job_name", + "command": "echo 'Hello files'", + "schedule": "@every 2m", + "tags": { + "role": "web" + }, + "processors": { + "files": { + "log_dir": "/var/log/mydir", + "forward": "true" + } + } +} +``` diff --git a/website/versioned_docs/version-v3/usage/processors/index.md b/website/versioned_docs/version-v3/usage/processors/index.md new file mode 100644 index 000000000..4ea78483c --- /dev/null +++ b/website/versioned_docs/version-v3/usage/processors/index.md @@ -0,0 +1,26 @@ +# Processors + +## Execution Processors + +Processor plugins are called when an execution response has been received. They are passed the resulting execution data and configuration parameters, this plugins can perform a variety of operations with the execution and it's very flexible and per Job, examples of operations this plugins can do: + +* Execution output storage, forwarding or redirection. +* Notification +* Monitoring + +For example, Processor plugins can be used to redirect the output of a job execution to different targets. + +Currently Dkron provides you with some built-in plugins but the list keeps growing. Some of the features previously implemented in the application will be progessively moved to plugins. + +### Built-in processors + +Dkron provides the following built-in processors: + +0. not specified - Store the output in the key value store (Slow performance, good for testing, default method) +0. log - Output the execution log to Dkron stdout (Good performance, needs parsing) +0. syslog - Output to the syslog (Good performance, needs parsing) +0. files - Output to multiple files (Good performance, needs parsing) + +[Dkro Pro](/pro/) provides you with several more processors. + +All plugins accepts one configuration option: `forward` Indicated if the plugin must forward the original execution output. This allows for chaining plugins and sending output to different targets at the same time. diff --git a/website/versioned_docs/version-v3/usage/processors/log.md b/website/versioned_docs/version-v3/usage/processors/log.md new file mode 100644 index 000000000..191b9fa49 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/processors/log.md @@ -0,0 +1,29 @@ +--- +title: Log Processor +--- + +Log processor writes the execution output to stdout/stderr + +## Configuration + +Parameters + +`forward: Forward the output to the next processor` + +Example + +```json +{ + "name": "job_name", + "command": "echo 'Hello log'", + "schedule": "@every 2m", + "tags": { + "role": "web" + }, + "processors": { + "log": { + "forward": "true" + } + } +} +``` diff --git a/website/versioned_docs/version-v3/usage/processors/syslog.md b/website/versioned_docs/version-v3/usage/processors/syslog.md new file mode 100644 index 000000000..8953a1045 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/processors/syslog.md @@ -0,0 +1,31 @@ +--- +title: Syslog Processor +--- + +Syslog processor writes the execution output to the system syslog daemon + +Note: Only work on linux systems + +## Configuration + +Parameters + +`forward: Forward the output to the next processor` + +Example + +```json +{ + "name": "job_name", + "command": "echo 'Hello syslog'", + "schedule": "@every 2m", + "tags": { + "role": "web" + }, + "processors": { + "syslog": { + "forward": "true" + } + } +} +``` diff --git a/website/versioned_docs/version-v3/usage/recovery.md b/website/versioned_docs/version-v3/usage/recovery.md new file mode 100644 index 000000000..1d1e40b73 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/recovery.md @@ -0,0 +1,104 @@ +--- +title: Outage recovery +--- + +## Outage Recovery + +Don't panic! This is a critical first step. + +Depending on your deployment configuration, it may take only a single server failure for cluster unavailability. Recovery requires an operator to intervene, but the process is straightforward. + +:::tip +This guide is for recovery from a Dkron outage due to a majority of server nodes in a datacenter being lost. If you are looking to add or remove servers, see the [clustering](/docs/usage/clustering) guide. +::: + +## Failure of a Single Server Cluster + +If you had only a single server and it has failed, simply restart it. A single server configuration requires the -bootstrap-expect=1 flag. If the server cannot be recovered, you need to bring up a new server. See the [clustering](/docs/usage/clustering) guide for more detail. + +In the case of an unrecoverable server failure in a single server cluster, data loss is inevitable since data was not replicated to any other servers. This is why a single server deploy is never recommended. + +## Failure of a Server in a Multi-Server Cluster + +If you think the failed server is recoverable, the easiest option is to bring it back online and have it rejoin the cluster with the same IP address, returning the cluster to a fully healthy state. Similarly, even if you need to rebuild a new Dkron server to replace the failed node, you may wish to do that immediately. Keep in mind that the rebuilt server needs to have the same IP address as the failed server. Again, once this server is online and has rejoined, the cluster will return to a fully healthy state. + +Both of these strategies involve a potentially lengthy time to reboot or rebuild a failed server. If this is impractical or if building a new server with the same IP isn't an option, you need to remove the failed server. Usually, you can issue a `dkron leave` command to remove the failed server if it's still a member of the cluster. + +If `dkron leave` isn't able to remove the server, you can use the `dkron raft remove-peer` command to remove the stale peer server on the fly with no downtime. + +You can use the `dkron raft list-peers` command to inspect the Raft configuration: + +``` +$ dkron raft list-peers +Node ID Address State Voter +dkron-server01.global 10.10.11.5:4647 10.10.11.5:4647 follower true +dkron-server02.global 10.10.11.6:4647 10.10.11.6:4647 leader true +dkron-server03.global 10.10.11.7:4647 10.10.11.7:4647 follower true +``` + +## Failure of Multiple Servers in a Multi-Server Cluster + +In the event that multiple servers are lost, causing a loss of quorum and a complete outage, partial recovery is possible using data on the remaining servers in the cluster. There may be data loss in this situation because multiple servers were lost, so information about what's committed could be incomplete. The recovery process implicitly commits all outstanding Raft log entries, so it's also possible to commit data that was uncommitted before the failure. + +See the section below for details of the recovery procedure. You simply include just the remaining servers in the raft/peers.json recovery file. The cluster should be able to elect a leader once the remaining servers are all restarted with an identical raft/peers.json configuration. + +Any new servers you introduce later can be fresh with totally clean data directories. + +In extreme cases, it should be possible to recover with just a single remaining server by starting that single server with itself as the only peer in the raft/peers.json recovery file. + +The raft/peers.json recovery file is final, and a snapshot is taken after it is ingested, so you are guaranteed to start with your recovered configuration. This does implicitly commit all Raft log entries, so should only be used to recover from an outage, but it should allow recovery from any situation where there's some cluster data available. + +## Manual Recovery Using peers.json + +To begin, stop all remaining servers. You can attempt a graceful leave, but it will not work in most cases. Do not worry if the leave exits with an error. The cluster is in an unhealthy state, so this is expected. + +The peers.json file will be deleted after Dkron starts and ingests this file. + +Using raft/peers.json for recovery can cause uncommitted Raft log entries to be implicitly committed, so this should only be used after an outage where no other option is available to recover a lost server. Make sure you don't have any automated processes that will put the peers file in place on a periodic basis. + +The next step is to go to the `data-dir` of each Dkron server. Inside that directory, there will be a raft/ sub-directory. We need to create a raft/peers.json file. It should look something like: + +```json +[ + { + "id": "node1", + "address": "10.1.0.1:4647" + }, + { + "id": "node2", + "address": "10.1.0.2:4647" + }, + { + "id": "node3", + "address": "10.1.0.3:4647" + } +] +``` + +Simply create entries for all remaining servers. You must confirm that servers you do not include here have indeed failed and will not later rejoin the cluster. Ensure that this file is the same across all remaining server nodes. + +At this point, you can restart all the remaining servers. In Dkron 0.5.5 and later you will see them ingest recovery file: + +``` +Recovery log placeholder +``` + +It should be noted that any existing member can be used to rejoin the cluster as the gossip protocol will take care of discovering the server nodes. + +At this point, the cluster should be in an operable state again. One of the nodes should claim leadership and emit a log like: + +`[INFO] Dkron: cluster leadership acquired` + +You can use the `dkron raft list-peers` command to inspect the Raft configuration: + +``` +$ dkron raft list-peers +Node ID Address State Voter +node1 node1 10.10.11.5:4647 follower true +node2 node2 10.10.11.6:4647 leader true +node3 node3 10.10.11.7:4647 follower true +``` + +* id (string: ) - Specifies the node ID of the server. This is the `name` of the node. + +* address (string: ) - Specifies the IP and port of the server in ip:port format. The port is the server's gRPC port used for cluster communications, typically `6868`. diff --git a/website/versioned_docs/version-v3/usage/retries.md b/website/versioned_docs/version-v3/usage/retries.md new file mode 100644 index 000000000..e07b3f147 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/retries.md @@ -0,0 +1,22 @@ +--- +title: Job retries +--- + +Jobs can be configured to retry in case of failure. + +## Configuration + +```json +{ + "name": "job1", + "schedule": "@every 10s", + "executor": "shell", + "executor_config": { + "command": "echo \"Hello from parent\"" + }, + "retries": 5 +} +``` + +In case of failure to run the job in one node, it will try to run the job again in that node until the retries count reaches the limit. + diff --git a/website/versioned_docs/version-v3/usage/storage.md b/website/versioned_docs/version-v3/usage/storage.md new file mode 100644 index 000000000..b614ef4f8 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/storage.md @@ -0,0 +1,7 @@ +--- +title: Embedded storage +--- + +Dkron has an embedded distributed KV store engine based on BuntDB. This works out of the box on each dkron server. + +This ensures a dead easy install and setup, basically run dkron and you will have a full working node. diff --git a/website/versioned_docs/version-v3/usage/target-nodes-spec.md b/website/versioned_docs/version-v3/usage/target-nodes-spec.md new file mode 100644 index 000000000..213ecbaf7 --- /dev/null +++ b/website/versioned_docs/version-v3/usage/target-nodes-spec.md @@ -0,0 +1,120 @@ +--- +title: Target nodes spec +weight: 10 +--- + +## Target nodes spec + +Default is for all nodes to execute each job. Dkron has the ability to run jobs in specific nodes by leveraging the use of tags. You can choose whether a job is run on a node or group of nodes by specifying tags and a count of target nodes having this tag do you want a job to run. + +The target node syntax is: + + "tag": "value[:count]" + +To achieve this Nodes and Jobs have tags, for example, having a node with the following tags: + +```json +{ + "tags": { + "dc": "dc1", + "expect": "3", + "port": "6868", + "region": "global", + "role": "dkron", + "rpc_addr": "10.88.94.129:6868", + "server": "true", + "version": "devel", + "my_role": "web" + } +} +``` + +:::tip +You can specify tags for nodes in the dkron config file or in the command line using `--tags` parameter +::: + +In case there is no matching nodes with the specified tags, the job will not run. + +Following some examples using different tag combinations: + +#### Target all nodes with a tag + +```json +{ + "name": "job_name", + "command": "/bin/true", + "schedule": "@every 2m", + "tags": { + "my_role": "web" + } +} +``` + +```mermaid +graph LR; + J("Job tags: #quot;my_role#quot;: #quot;web#quot;") -->|Run Job|N1["Node1 tags: #quot;my_role#quot;: #quot;web#quot;"] + J -->|Run Job|N2["Node2 tags: #quot;my_role#quot;: #quot;web#quot;"] + J -->|Run Job|N3["Node2 tags: #quot;my_role#quot;: #quot;web#quot;"] +``` + +#### Target only one nodes of a group of nodes with a tag + +```json +{ + "name": "job_name", + "command": "/bin/true", + "schedule": "@every 2m", + "tags": { + "my_role": "web:1" + } +} +``` + +```mermaid +graph LR; + J("Job tags: #quot;my_role#quot;: #quot;web:1#quot;") -->|Run Job|N1["Node1 tags: #quot;my_role#quot;: #quot;web#quot;"] + J -.- N2["Node2 tags: #quot;my_role#quot;: #quot;web#quot;"] + J -.- N3["Node2 tags: #quot;my_role#quot;: #quot;web#quot;"] +``` + +Dkron will try to run the job in the amount of nodes indicated by that count having that tag. + +### Details and limitations + +#### Reserved tags + +The `region` tag is reserved for multi-region setup purposes and should not be used for any other purpose. + +Dkron will always run jobs in nodes with the same region tag. + +You should use an alternative name. + +#### Combinations + +Tags specified in a Job are combined using `AND`, therefore a job that specifies several tags like: + +```json +{ + "tags": { + "my_role": "web", + "role": "dkron" + } +} +``` + +Will try to run the job in nodes that have all speciefied tags. + +#### Tags Limit + +There is no limit in the tags that a job can have but having a Job with several tags with count like: + +```json +{ + "tags": { + "my_role": "web:1", + "role": "dkron:2" + } +} +``` + +Will try to run the job in nodes that have all specified tags and using the lowest count. In the last example, it will run in **one** node having `"my_role": "web"` and `"role": "dkron"` tag, even if there is more than one node with these tags. diff --git a/website/versioned_docs/version-v3/usage/upgrade.md b/website/versioned_docs/version-v3/usage/upgrade.md new file mode 100644 index 000000000..e44a5795e --- /dev/null +++ b/website/versioned_docs/version-v3/usage/upgrade.md @@ -0,0 +1,26 @@ +--- +title: Upgrade methods +--- + +Use one of the following methods (depending on the changes) to upgrade a cluster to a newer version. + +### Rolling upgrade + +Use the following procedure to rotate all cluster nodes, one server at a time: + +1. Add a new server to the cluster with a configuration that joins them to the existing cluster. +1. Stop dkron service on one of the old servers, if it was the leader allow a new leader to be elected. Note that it is better to remove the current leader at the end, to ensure a leader is elected from the new nodes. +1. Use `dkron raft list-peers` to list current cluster nodes. +1. Use `dkron raft remove-peer` to forcefully remove the old server. +1. Repeat steps above until all old cluster nodes have been upgraded. + +### Backup & Restore + +Use the `/restore` API endpoint to restore a previously exported jobs file + +``` +curl localhost:8080/v1/jobs > backup.json +curl localhost:8080/v1/restore --form 'file=@backup.json' +``` + +This will restore all jobs and counters as they were in the export file. diff --git a/website/versioned_sidebars/version-v3-sidebars.json b/website/versioned_sidebars/version-v3-sidebars.json new file mode 100644 index 000000000..caea0c03b --- /dev/null +++ b/website/versioned_sidebars/version-v3-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/website/versions.json b/website/versions.json index 8b7e89402..d0cec5f0c 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1,4 +1,5 @@ [ + "v3", "v2", "v1" ]