The timeout corresponds to how long a function can run for until it is executed. It is important for preventing misuse in distributed systems.
There are several places where a timeout can be configured for your function, in each place this is done through the use of environmental variables.
-
Function timeout
-
read_timeout
- time allowed fo the function to read a request over HTTP -
write_timeout
- time allowed for the function to write a response over HTTP -
exec_timeout
- the maximum duration a function can run before being terminated
The API Gateway has a default of 20 seconds, so let's test out setting a shorter timeout on a function.
$ faas-cli new --lang python3 sleep-for --prefix="<your-docker-username-here>"
Edit handler.py
:
import time
import os
def handle(req):
"""handle a request to the function
Args:
req (str): request body
"""
sleep_duration = int(os.getenv("sleep_duration", "10"))
print("Starting to sleep for %d" % sleep_duration)
time.sleep(sleep_duration) # Sleep for a number of seconds
print("Finished the sleep")
Now edit the sleep-for.yml
file and add these environmental variables:
provider:
name: faas
gateway: http://127.0.0.1:8080
functions:
sleep-for:
lang: python3
handler: ./sleep-for
image: <your-docker-username-here>/sleep-for:0.1
environment:
sleep_duration: 10
read_timeout: 5
write_timeout: 5
exec_timeout: 5
Use the CLI to build, push, deploy and invoke the function.
$ echo | faas-cli invoke sleep-for
Server returned unexpected status code: 500 - Can't reach service: sleep-for
You should see it terminate without printing the message.
Now set sleep_duration
to a lower number like 2
and run faas-cli deploy
again. You don't need to rebuild the function when editing the function's YAML file.
$ echo | faas-cli invoke sleep-for
Starting to sleep for 2
Finished the sleep
- API Gateway
This is the maximum timeout duration as set at the gateway, it will override the function timeout. At the time of writing the maximum timeout is configured at "20s", but can be configured to a longer or shorter value.
To update the gateway value set read_timeout
and write_timeout
in the docker-compose.yml
file for the gateway
and faas-swarm
service then run ./deploy_stack.sh
.
It is useful to be able to control how a function behaves at runtime, we can do that in at least two ways:
- Set environmental variables at deployment time
We did this with write_debug
and exec_timeout
- you can also set any custom environmental variables you want here too - for instance if you wanted to configure a language for your hello world function you may introduce a spoken_language
variable.
- Use querystring and HTTP headers
The other option which is more dynamic and can be altered at a per-request level is the use of querystrings and HTTP headers, both can be passed through the faas-cli
or curl
.
These headers become exposed through environmental variables so they are easy to consume within your function. So any header is prefixed with Http_
and all -
hyphens are replaced with an _
underscore.
Let's try it out with a querystring and a function that lists off all environmental variables.
- Deploy a function that prints environmental variables using a built-in BusyBox command:
$ faas-cli deploy --name env --fprocess="env" --image="functions/alpine:latest" --network=func_functions
- Invoke the function with a querystring:
$ echo "" | faas-cli invoke env --query workshop=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=05e8db360c5a
fprocess=env
HOME=/root
Http_Connection=close
Http_Content_Type=text/plain
Http_X_Call_Id=cdbed396-a20a-43fe-9123-1d5a122c976d
Http_X_Forwarded_For=10.255.0.2
Http_X_Start_Time=1519729562486546741
Http_User_Agent=Go-http-client/1.1
Http_Accept_Encoding=gzip
Http_Method=POST
Http_ContentLength=-1
Http_Path=/function/env
...
Http_Query=workshop=1
...
In Python code you'd type in os.getenv("Http_Query")
.
- Now invoke it with a header:
$ echo "" | curl http://127.0.0.1:8080/function/env --header "X-Output-Mode: json"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=05e8db360c5a
fprocess=env
HOME=/root
Http_X_Call_Id=8e597bcf-614f-4ca5-8f2e-f345d660db5e
Http_X_Forwarded_For=10.255.0.2
Http_X_Start_Time=1519729577415481886
Http_Accept=*/*
Http_Accept_Encoding=gzip
Http_Connection=close
Http_User_Agent=curl/7.55.1
Http_Method=GET
Http_ContentLength=0
Http_Path=/function/env
...
Http_X_Output_Mode=json
...
In Python code you'd type in os.getenv("Http_X_Output_Mode")
.
You can see that all other HTTP context is also provided such as Content-Length
when the Http_Method
is a POST
, the User_Agent
, Cookies and anything else you'd expect to see from a HTTP request.
The way OpenFaaS watchdog works is that it passses in the HTTP request and reads a HTTP response via the standard I/O streams stdin
and stdout
accordingly.
This means that your process does not need to know anything about the web or HTTP.
An interesting case is when it finishes with a non-zero exit code and stderr
is not empty.
By default function stdout/stderr
is combined and stderr
is not printed to the logs.
Lets check that with the hello-openfaas
function from Lab 3.
Change the handler.py
code to
import sys
import json
def handle(req):
sys.stderr.write("This should be an error message.\n")
return json.dumps({"hello": "world"})
Now invoke the function with
echo | faas invoke hello-openfaas
You should see the combined output:
This should be an error message.
{"hello": "world"}
Note: If you check the container logs with
docker service logs hello-openfaas
you should not see the stderr output.
In the example we need the function to return valid JSON that can be parsed. Unfortunately the log message makes the output invalid,
so we need to redirect the messages from stderr to the container's logs.
OpenFaaS provides a solution so you can print the error messages to the logs and keep the function response clear, returning only stdout
.
You should use the combine_output
flag for that purposes.
Let's try it. Open the hello-openfaas.yaml
file and add those lines:
environment:
combine_output: false
Push, deploy and invoke the function.
The output should be:
{"hello": "world"}
Check the container logs for stderr
. You should see a message like:
hello-openfaas.1.2xtrr2ckkkth@linuxkit-025000000001 | 2018/04/03 08:35:24 stderr: This should be an error message.
In Python you can find environmental variables through the os.getenv(key, default_value)
function or os.environ
array after importing the os
package. The OpenFaaS watchdog provides all HTTP context to your function through environmental variables. They can be used at deployment time or at runtime to alter the behaviour of your code.
i.e.
import os
def handle(st):
print os.getenv("Http_Method") # will be "NoneType" if empty
print os.getenv("Http_Method", "GET") # provide a default of "GET" if empty
print os.environ["Http_Method"] # throws an exception is not present
print os.environ # array of environment
Now move onto Lab 4b