API

K8sDeputy.DeputyType
Deputy(; 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 when shutdown!(::Deputy) is called.
  • shutdown_handler_timeout::Period (optional): Specifies the maximum execution duration of a shutdown_handler.
source
K8sDeputy.serve!Function
K8sDeputy.serve!(deputy::Deputy, [host], [port::Integer]; kwargs...) -> HTTP.Server

Starts 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 (has readied!(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 to Sockets.localhost.
  • port::Integer (optional): The port to listen on. Defaults to the port number specified by the environmental variable DEPUTY_HEALTH_CHECK_PORT, otherwise 8081.

Any kwargs provided are passed to HTTP.serve!.

source
K8sDeputy.readied!Function
readied!(deputy::Deputy) -> Nothing

Mark the application as "ready". Sets the readiness endpoint to respond with successful responses.

source
K8sDeputy.shutdown!Function
shutdown!(deputy::Deputy) -> Nothing

Initiates a shutdown of the application by:

  1. Mark the application as shutting down ("non-live").
  2. Executing the deputy's shutdown_handler (if defined).
  3. 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.

source
K8sDeputy.graceful_terminatorFunction
graceful_terminator(f; set_entrypoint::Bool=true) -> Nothing

Register 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 running graceful_terminate in another Julia process. Users who want to utilize graceful_terminator in multiple Julia processes should use set_entrypoint=false and specify process IDs when calling graceful_terminate. Defaults to true.

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.

Note

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.

source
K8sDeputy.graceful_terminateFunction
graceful_terminate(pid::Int32=entrypoint_pid(); wait::Bool=true) -> Nothing

Initiates the execution of the graceful_terminator user callback in the process pid. See graceful_terminator for more details.

source