API
K8sDeputy.Deputy — TypeDeputy(; shutdown_handler=nothing, shutdown_handler_timeout::Period=Second(5))Construct an application Deputy which provides health check endpoints.
Keywords
shutdown_handler(optional): A zero-argument function which allows the user to provide a custom callback function for whenshutdown!(::Deputy)is called.shutdown_handler_timeout::Period(optional): Specifies the maximum execution duration of ashutdown_handler.
K8sDeputy.serve! — FunctionK8sDeputy.serve!(deputy::Deputy, [host], [port::Integer]; kwargs...) -> HTTP.ServerStarts a non-blocking HTTP.Server responding to requests to deputy health checks. The following health check endpoints are available:
/health/live: Is the server is alive/running?/health/ready: Is the server ready (hasreadied!(deputy)been called)?
These endpoints will respond with HTTP status 200 OK on success or 503 Service Unavailable on failure.
Arguments
host(optional): The address to listen to for incoming requests. Defaults toSockets.localhost.port::Integer(optional): The port to listen on. Defaults to the port number specified by the environmental variableDEPUTY_HEALTH_CHECK_PORT, otherwise8081.
Any kwargs provided are passed to HTTP.serve!.
K8sDeputy.readied! — Functionreadied!(deputy::Deputy) -> NothingMark the application as "ready". Sets the readiness endpoint to respond with successful responses.
K8sDeputy.shutdown! — Functionshutdown!(deputy::Deputy) -> NothingInitiates a shutdown of the application by:
- Mark the application as shutting down ("non-live").
- Executing the deputy's
shutdown_handler(if defined). - Exiting the current Julia process.
If a deputy.shutdown_handler is defined it must complete within the deputy.shutdown_handler_timeout or a warning will be logged and the Julia process will immediately exit. Any exceptions that occur in the deputy.shutdown_handler will also be logged and result in the Julia process exiting.
A shutdown_handler may optionally call exit if a user wants to specify the exit status. By default shutdown! uses an exit status of 1.
K8sDeputy.graceful_terminator — Functiongraceful_terminator(f; set_entrypoint::Bool=true) -> NothingRegister a zero-argument function to be called when graceful_terminate is called targeting this process. The user-defined function f is expected to call exit to terminate the Julia process. The graceful_terminator function is only allowed to be called once within a Julia process.
Keywords
set_entrypoint::Bool(optional): Sets the calling Julia process as the "entrypoint" to be targeted by default when runninggraceful_terminatein another Julia process. Users who want to utilizegraceful_terminatorin multiple Julia processes should useset_entrypoint=falseand specify process IDs when callinggraceful_terminate. Defaults totrue.
Examples
app_status = AppStatus()
graceful_terminator(() -> shutdown!(app_status))Kubernetes Setup
When using Kubernetes (K8s) you can enable graceful termination of a Julia process by defining a pod preStop container hook. Typically, K8s initiates graceful termination via the TERM signal but as Julia forcefully terminates when receiving this signal and Julia does not support user-defined signal handlers we utilize preStop instead.
Using the supervise entrypoint
K8sDeputy provides a bash script in bin/superviser.sh which handles the TERM signal and sends the "terminate" message to the graceful termination socket. This can be used as the ENTRYPOINT for your Docker image. For example, after installing K8sDeputy in the active Julia project:
RUN julia --color=yes -e 'using K8sDeputy; K8sDeputy.install_supervise_shim("/usr/bin")'
ENTRYPOINT ["/usr/bin/supervise.sh"]The command/script to run and any arguments it requires should be passed in via args in your K8s Container spec; anything you specify for command in the Container spec will override the container's entrypoint.
The entrypoint script requires netcat, which is not present in e.g. debian-slim images. In particular, it requires the "OpenBSD" flavor (available as netcat-openbsd via apt-get). The script will fail before starting if command -v nc fails.
It also uses jq to generate JSON-formatted log messages with timestamps. This is not required (it uses a more fragile fallback) but strongly recommended.
Using the pre-stop hook
The following K8s pod manifest snippet will specify K8s to call the user-defined function specified by the graceful_terminator:
spec:
containers:
- lifecycle:
preStop:
exec:
command: ["julia", "-e", "using K8sDeputy; graceful_terminate()"]Additionally, the entrypoint for the container should also not directly use the Julia process as the init process (PID 1). Instead, users should define their entrypoint similarly to ["/bin/sh", "-c", "julia entrypoint.jl; sleep 1"] as this allows for both the Julia process and the preStop process to cleanly terminate.
K8sDeputy.graceful_terminate — Functiongraceful_terminate(pid::Int32=entrypoint_pid(); wait::Bool=true) -> NothingInitiates the execution of the graceful_terminator user callback in the process pid. See graceful_terminator for more details.