Skip to content

Instantly share code, notes, and snippets.

@hugobarauna
Created June 20, 2025 11:49
Show Gist options
  • Save hugobarauna/02ea6cedd9d1ea44bab92187b67684f8 to your computer and use it in GitHub Desktop.
Save hugobarauna/02ea6cedd9d1ea44bab92187b67684f8 to your computer and use it in GitHub Desktop.
Automatically clustering a Livebook notebook with a Phoenix app (running on Fly.io)

Clustering with Teams template

Mix.install([
  {:kino, "~> 0.16.0"},
  {:req, "~> 0.5.10"}
])

Clustering with Teams

defmodule Fly do
  @fly_token System.fetch_env!("LB_FLY_TOKEN")

  def machines(fly_app_name) do
    case Req.get(new(), url: "/v1/apps/#{fly_app_name}/machines") do
      {:ok, %Req.Response{status: 200} = response} ->
        {:ok, response.body}

      {:ok, %Req.Response{status: status, body: body}} ->
        {:error,
         "Error when calling Fly API.\n HTTP response status: #{status}\n HTTP response body: \n\t#{body}"}

      {:error, exception} ->
        {:error, "Exception calling Fly API: #{inspect(exception)}"}
    end
  end

  def new() do
    Req.new(
      base_url: "https://api.machines.dev",
      auth: {:bearer, @fly_token}
    )
  end
end
defmodule TeamsClustering do
  @fly_app_name System.fetch_env!("LB_TEAMS_FLY_APP")
  @teams_cookie System.fetch_env!("LB_TEAMS_COOKIE")
  @app_server_env System.fetch_env!("LB_APP_SERVER_ENV")

  def cluster() do
    Node.set_cookie(teams_cookie())

    case Node.connect(teams_node()) do
      true -> :ok
      _ -> {:error, "Tried to connect to #{inspect(teams_node())}"}
    end
  end

  def teams_cookie, do: String.to_atom(@teams_cookie)

  def teams_node(), do: teams_node(@app_server_env)

  defp teams_node("dev"), do: :"[email protected]"

  defp teams_node(env) when env in ["staging", "prod"] do
    {:ok, [fly_machine | _]} = Fly.machines(@fly_app_name)
    ip = fly_machine["private_ip"]
    :"#{@fly_app_name}-#{image_id(fly_machine)}@#{ip}"
  end

  defp image_id(fly_machine) do
    image_tag = fly_machine["image_ref"]["tag"]

    [image_id] = Regex.run(~r/.*-(.*)/, image_tag, capture: :all_but_first)
    image_id
  end
end
import Kino.Shorts
clustering_flame = frame(placeholder: false) |> Kino.render()

Kino.Frame.render(clustering_flame, "Connecting to Teams...")

case TeamsClustering.cluster() do
  :ok ->
    Kino.Frame.render(clustering_flame, "Connected to Teams ✅")

  {:error, error_message} ->
    Kino.Frame.clear(clustering_flame)

    error_message =
      Kino.Text.new("Problem while connecting to Teams: #{error_message}", style: [color: "red"])

    Kino.render(error_message)
    Kino.interrupt!(:error, "Try again?")
end
teams_node = TeamsClustering.teams_node()
teams_cookie = TeamsClustering.teams_cookie()
require Kino.RPC
node = teams_node
Node.set_cookie(node, teams_cookie)
Kino.RPC.eval_string(node, ~S":ok", file: __ENV__.file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment