|
| 1 | +--- |
| 2 | +title: Keep your functions running to schedule with the cron-connector. |
| 3 | +description: "In this post Martin will show you how to schedule your functions with the cron-connector, for regular tasks and scheduled jobs" |
| 4 | +date: 2020-05-14 |
| 5 | +image: /images/2020-05-14-schedule-your-functions/accuracy-alarm-clock-analogue-business.jpg |
| 6 | +categories: |
| 7 | + - cron |
| 8 | + - kubernetes |
| 9 | + - serverless |
| 10 | + - linux |
| 11 | + - tutorial |
| 12 | + - example |
| 13 | +author_staff_member: martin |
| 14 | +dark_background: true |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +In this post Martin will show you how schedule your functions with the cron-connector, for regular tasks and scheduled jobs. |
| 19 | + |
| 20 | +One of the things we need very often is our functions to be executed on a schedule. This is where the cron-connector shines. For our example we will create `Lockbot` which will lock conversation on outdated issues. With the `cron-connector` your functions will run on time. |
| 21 | + |
| 22 | +## Motivation |
| 23 | + |
| 24 | +OpenFaaS provides a very convenient and flexible way to quickly write and expose your code to be used by external systems. Often times those systems don't have a hook to which you can schedule function execution. Also very often we need to run those functions without external systems, we just want our code to be ran on an interval. |
| 25 | + |
| 26 | +For those purposes [connector-sdk](https://github.com/openfaas-incubator/connector-sdk) was created. It provides the means to quickly adapt different systems to invoke functions. Some of them are [Kafka](https://github.com/openfaas-incubator/kafka-connector), [NATS](https://github.com/openfaas-incubator/nats-connector), [MQTT](https://github.com/openfaas-incubator/mqtt-connector) and [many more](https://docs.openfaas.com/reference/triggers/). |
| 27 | + |
| 28 | +Now we need our functions to to be invoked on an interval. [Cron](https://en.wikipedia.org/wiki/Cron) is often used on GNU/Linux systems to execute tasks to a pre-defined schedule. In Kubernetes, this was abstracted into the `CronJob` object. For any OpenFaaS cluster, the [cron-connector](https://github.com/openfaas-incubator/cron-connector) uses the `connector-sdk` to provide the same functionality, running our functions on a predefined schedule. |
| 29 | + |
| 30 | +Now lets roll up our sleeves and see how we can execute our OpenFaaS functions on time. |
| 31 | + |
| 32 | +## Prerequisites |
| 33 | + |
| 34 | +Before we start we need a couple of tools to help us quickly set up our environment: |
| 35 | + |
| 36 | +* [docker](https://docs.docker.com/get-docker/) - the container runtime used in this post |
| 37 | +* [kubernetes](https://kind.sigs.k8s.io/docs/user/quick-start/) - cluster which will manage our containers |
| 38 | +* [arkade](https://github.com/alexellis/arkade) - one line installation of applications with which we will install OpenFaaS |
| 39 | +* [faas-cli](https://docs.openfaas.com/cli/install/) - the CLI which communicates with the OpenFaaS gateway |
| 40 | +* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) - the CLI which communicates with your Kubernetes cluster |
| 41 | + |
| 42 | +## Prepare environment |
| 43 | + |
| 44 | +Before showing you how to run our functions on a schedule, first we need to set up the environment with the tools mentioned above. |
| 45 | + |
| 46 | +### Create cluster |
| 47 | + |
| 48 | +First thing we need is running Kubernetes cluster: |
| 49 | + |
| 50 | +```bash |
| 51 | +kind create cluster |
| 52 | +``` |
| 53 | + |
| 54 | +Wait for the installation to finish and verify the pods in the `kube-system` namespace are `1/1`: |
| 55 | + |
| 56 | +```bash |
| 57 | +kubectl get pods -n kube-system |
| 58 | +``` |
| 59 | + |
| 60 | +### Install OpenFaaS |
| 61 | + |
| 62 | +With `arkade` the installation of OpenFaaS boils down to single line command: |
| 63 | + |
| 64 | +```bash |
| 65 | +arkade install openfaas |
| 66 | +``` |
| 67 | + |
| 68 | +Wait for the OpenFaaS gateway to be ready: |
| 69 | + |
| 70 | +```bash |
| 71 | +kubectl rollout status -n openfaas deploy/gateway |
| 72 | +``` |
| 73 | + |
| 74 | +Follow the instructions provided by `arkade` after the installation to set up the `faas-cli`. Also we will use Dockerhub to store the images so before we continue set `OPENFAAS_PREFIX`: |
| 75 | + |
| 76 | +```bash |
| 77 | +export OPENFAAS_PREFIX="<dockerhub_username>" |
| 78 | +``` |
| 79 | + |
| 80 | +## Schedule the functions |
| 81 | + |
| 82 | +After we have the environment up and running, now we can install the cron-connector and schedule our functions to run at specific intervals. |
| 83 | + |
| 84 | +### Deploy the cron-connector |
| 85 | + |
| 86 | +In order to install our `cron-connector` and schedule a function run the following command: |
| 87 | + |
| 88 | +```bash |
| 89 | +arkade install cron-connector |
| 90 | +``` |
| 91 | + |
| 92 | +Wait for the `cron-connector` to be deployed: |
| 93 | + |
| 94 | +```bash |
| 95 | +kubectl rollout status -n openfaas deploy/cron-connector |
| 96 | +``` |
| 97 | + |
| 98 | +The cron connector has a [helm chart](https://github.com/openfaas/faas-netes/tree/master/chart/cron-connector) which you can explore. |
| 99 | + |
| 100 | +## How to schedule |
| 101 | + |
| 102 | +The schedule of the function is configured through the `schedule` annotation following the normal cron syntax `* * * * *`. See [this](https://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/) blog post for some examples of the cron format. |
| 103 | + |
| 104 | +Couple of quick cron schedule examples are: |
| 105 | + |
| 106 | +* `*/5 * * * *` - will run once every five minutes |
| 107 | +* `0 0 */1 * *` - will run once every day of the month at 00:00 |
| 108 | +* `0 0 * * */7` - will run once a week on the seventh day at 00:00 |
| 109 | + |
| 110 | +The cron-connector recognizes which functions should be ran on a schedule by annotation `topic` with value `cron-function`, we can add this annotation to the function's configurtion or during deployment |
| 111 | + |
| 112 | +Now lets go head and create a function which will run on a schedule. |
| 113 | + |
| 114 | +## Meet Lockbot |
| 115 | + |
| 116 | +The `Lockbot` will run once per day at `00:00` and will check for old GitHub issues. The issue will be locked in case it is older than the days we will chose. For our example we can go with 3 months, which is roughly 90 days. |
| 117 | + |
| 118 | +### Get the function template from store |
| 119 | + |
| 120 | +The OpenFaaS framework has rich variety of language templates which you can use to write your own functions. In our example we will use Python 3 with the Debian based template `python3-flask-debian`: |
| 121 | + |
| 122 | +```bash |
| 123 | +faas-cli template store pull python3-flask-debian |
| 124 | +``` |
| 125 | + |
| 126 | +### Generate the function |
| 127 | + |
| 128 | +In order to generate the function's backbone we will run the following command: |
| 129 | + |
| 130 | +```bash |
| 131 | +faas-cli new lockbot --lang python3-flask-debian |
| 132 | +``` |
| 133 | + |
| 134 | +### Generate access token |
| 135 | + |
| 136 | +Go to the [personal access tokens](https://github.com/settings/tokens) page in GitHub and press the `Generate new token` button. The bot will use this to authenticate to the repository you chose. |
| 137 | + |
| 138 | +Now copy the generated token and create `auth-token` secret using the `faas-cli` where you need to replace the `<token>` with the actual copied access token: |
| 139 | + |
| 140 | +```bash |
| 141 | +faas-cli secret create auth-token --from-literal='<token>' |
| 142 | +``` |
| 143 | + |
| 144 | +### Configure the function |
| 145 | + |
| 146 | +Open the function's configuration file called `stack.yml`. Append the following lines to the file: |
| 147 | + |
| 148 | +```yml |
| 149 | + environment: |
| 150 | + github_repository: <repository> |
| 151 | + inactive_days: 90 |
| 152 | + exec_timeout: 30s |
| 153 | + read_timeout: 30s |
| 154 | + write_timeout: 30s |
| 155 | + secrets: |
| 156 | + - auth-token |
| 157 | + annotations: |
| 158 | + topic: cron-function |
| 159 | + schedule: "0 0 */1 * *" |
| 160 | +``` |
| 161 | +
|
| 162 | +> The schedule used `0 0 */1 * *` means once every day of the month at 00:00 |
| 163 | + |
| 164 | +We have increased the default timeout of the function to `30s`. Replace `<repository>` with one of your own repositories. The whole `stack.yml` file should look something like this: |
| 165 | + |
| 166 | +```yml |
| 167 | +version: 1.0 |
| 168 | +provider: |
| 169 | + name: openfaas |
| 170 | + gateway: http://127.0.0.1:31112 |
| 171 | +functions: |
| 172 | + lockbot: |
| 173 | + lang: python3-flask-debian |
| 174 | + handler: ./lockbot |
| 175 | + image: martindekov/lockbot:latest |
| 176 | + environment: |
| 177 | + github_repository: push2 |
| 178 | + inactive_days: 90 |
| 179 | + exec_timeout: 30s |
| 180 | + read_timeout: 30s |
| 181 | + write_timeout: 30s |
| 182 | + secrets: |
| 183 | + - auth-token |
| 184 | + annotations: |
| 185 | + topic: cron-function |
| 186 | + schedule: "0 0 */1 * *" |
| 187 | +``` |
| 188 | + |
| 189 | +> The `image` and `repository` should be different |
| 190 | + |
| 191 | +### Augment the code |
| 192 | + |
| 193 | +Inside the `lockbot` folder you can see the `handler.py` file which contains the `handle` method which is the entry point to your function. Replace what's inside with the lockbot's code: |
| 194 | + |
| 195 | +```python |
| 196 | +import os |
| 197 | +from datetime import datetime |
| 198 | +from github import Github |
| 199 | +
|
| 200 | +
|
| 201 | +def get_issues(): |
| 202 | + desired_repo = os.getenv("github_repository") |
| 203 | + desired_days = int(os.getenv("inactive_days")) |
| 204 | + auth = None |
| 205 | + with open("/var/openfaas/secrets/auth-token") as file: |
| 206 | + auth = Github(file.read().strip()) |
| 207 | +
|
| 208 | + issues_for_lock = [] |
| 209 | + for repo in auth.get_user().get_repos(): |
| 210 | + if repo.name == desired_repo: |
| 211 | + for issue in repo.get_issues(): |
| 212 | + if not issue.pull_request and not issue.locked: |
| 213 | + last_comment = issue.get_comments()[ |
| 214 | + issue.comments-1].updated_at |
| 215 | + difference = datetime.now() - datetime(last_comment.year, |
| 216 | + last_comment.month, |
| 217 | + last_comment.day) |
| 218 | + if difference.days > desired_days: |
| 219 | + issues_for_lock.append(issue) |
| 220 | +
|
| 221 | + return issues_for_lock |
| 222 | +
|
| 223 | +
|
| 224 | +def lock(issues): |
| 225 | + response = "no unlocked inactive issues" |
| 226 | + if len(issues) > 0: |
| 227 | + response = "issues locked:" |
| 228 | + for issue in issues: |
| 229 | + issue.lock("off-topic") |
| 230 | + response = response + f"\n{issue.title}" |
| 231 | + return response |
| 232 | +
|
| 233 | +
|
| 234 | +def handle(req): |
| 235 | + issues = get_issues() |
| 236 | + response = lock(issues) |
| 237 | + return response |
| 238 | +``` |
| 239 | + |
| 240 | +Finally add the Github SDK to the `requirements.txt` which is in the same folder as `handler.py`: |
| 241 | + |
| 242 | +```text |
| 243 | +PyGithub |
| 244 | +``` |
| 245 | + |
| 246 | +The function will fetch the issues from the repository you chose in the configuration section then it will filter them and calculate, using the date when they were opened, whether they are older than the 90 days we chose. If this is the case the lockbot will authenticate itself against the GitHub API with the access token we generated and will lock the issue from further conversation, marking it as `off-topic`. The lockbot will respond with the locked issues or the lack of such. |
| 247 | + |
| 248 | +### Deploy the function |
| 249 | + |
| 250 | +Deploy lockbot so that it can start locking inactive issues on your GitHub repositories: |
| 251 | + |
| 252 | +```bash |
| 253 | +faas up -f lockbot.yml |
| 254 | +``` |
| 255 | + |
| 256 | +A full working example of the function can be see in the [lockbot](https://github.com/martindekov/lockbot) repository. |
| 257 | + |
| 258 | +>Note: To see the function in action you can directly invoke it using the UI or the CLI. |
| 259 | + |
| 260 | +## Wrapping up |
| 261 | + |
| 262 | +In this post we have shown how you can run minimal OpenFaaS environment with simple function and how with the help of the cron-connector you can execute the function on a desired schedule. |
| 263 | + |
| 264 | +Learn how to create your own functions with the [OpenFaaS workshop](https://github.com/openfaas/workshop) and all the ways you can [trigger them](https://docs.openfaas.com/reference/triggers/). We used one of the templates from the store in our example, but you'll find many others, just run `faas-cli template store list` or view the [Template Documentation](https://docs.openfaas.com/cli/templates/) and make sure your functions always run on time! |
0 commit comments