Scaling on Kubernetes
Scale a Kubernetes deployment's replica count with a small Oban.Pro.Cloud module built on
Req. The API server, token, and deployment are resolved in init/1, and scale/2 patches the
deployment's scale subresource.
def deps do
[
{:req, "~> 0.5"}
]
endThe Module
The Oban.Pro.Cloud.init/1 callback reads the API server and bearer token from the system
environment, leaving the namespace and deployment to pass through as options. The
Oban.Pro.Cloud.scale/2 callback sends a strategic merge patch to set replicas and returns
{:ok, conf} so the scaler can track the response.
defmodule MyApp.Kubernetes do
@behaviour Oban.Pro.Cloud
@impl Oban.Pro.Cloud
def init(opts) do
opts
|> Keyword.put_new(:api_server, System.fetch_env!("KUBERNETES_API_SERVER"))
|> Keyword.put_new(:bearer_token, System.fetch_env!("KUBERNETES_TOKEN"))
|> Map.new()
end
@impl Oban.Pro.Cloud
def scale(desired, conf) do
url =
"#{conf.api_server}/apis/apps/v1/namespaces/#{conf.namespace}/" <>
"deployments/#{conf.deployment}/scale"
headers = [{"content-type", "application/strategic-merge-patch+json"}]
body = JSON.encode!(%{spec: %{replicas: desired}})
case Req.patch(url, auth: {:bearer, conf.bearer_token}, headers: headers, body: body) do
{:ok, %{status: status}} when status in 200..299 -> {:ok, conf}
{:ok, response} -> {:error, response}
{:error, reason} -> {:error, reason}
end
end
endRunning inside the cluster, point api_server at https://kubernetes.default.svc and read the
token from the mounted service account at /var/run/secrets/kubernetes.io/serviceaccount/token.
The service account needs patch on the deployments/scale subresource through a role binding.
Using It
Point a scaler at the module and pass the deployment it should scale:
{DynamicScaler,
scalers: [range: 1..5, cloud: {MyApp.Kubernetes, namespace: "default", deployment: "workers"}]}