mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-05 04:24:21 +08:00
Add support for DNS clustering (#2578)
This commit is contained in:
parent
d6848ed106
commit
d4752ebd52
6 changed files with 114 additions and 44 deletions
|
@ -14,7 +14,7 @@ It automatically sets up a cluster to run on Fly using DNS configuration. Behind
|
|||
|
||||
Sets up a cluster using DNS for queries for A/AAAA records to discover new nodes. Additionally, you must additionally set the following env vars:
|
||||
|
||||
* `LIVEBOOK_NODE=livebook_server@IP`, where `IP` is the machine IP of each deployed node
|
||||
* `LIVEBOOK_NODE=livebook_server@MACHINE_IP`, where `MACHINE_IP` is the machine IP of each deployed node
|
||||
|
||||
* You must set `LIVEBOOK_SECRET_KEY_BASE` and `LIVEBOOK_COOKIE` to different random values (use `openssl rand -base64 48` to generate said values)
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ defmodule Livebook.Hubs.Dockerfile do
|
|||
types = %{
|
||||
deploy_all: :boolean,
|
||||
docker_tag: :string,
|
||||
clustering: Ecto.ParameterizedType.init(Ecto.Enum, values: [:fly_io]),
|
||||
clustering: Ecto.ParameterizedType.init(Ecto.Enum, values: [:fly_io, :dns]),
|
||||
zta_provider: Ecto.ParameterizedType.init(Ecto.Enum, values: zta_types),
|
||||
zta_key: :string
|
||||
}
|
||||
|
@ -151,15 +151,30 @@ defmodule Livebook.Hubs.Dockerfile do
|
|||
{secret_key_base, cookie} = deterministic_skb_and_cookie(secret_key)
|
||||
|
||||
startup =
|
||||
if config.clustering == :fly_io do
|
||||
"""
|
||||
# --- Clustering ---
|
||||
case to_string(config.clustering) do
|
||||
"fly_io" ->
|
||||
"""
|
||||
# --- Clustering ---
|
||||
|
||||
# Set the same Livebook secrets across all nodes
|
||||
ENV LIVEBOOK_SECRET_KEY_BASE "#{secret_key_base}"
|
||||
ENV LIVEBOOK_COOKIE "#{cookie}"
|
||||
ENV LIVEBOOK_CLUSTER "fly"
|
||||
"""
|
||||
# Set the same Livebook secrets across all nodes
|
||||
ENV LIVEBOOK_SECRET_KEY_BASE "#{secret_key_base}"
|
||||
ENV LIVEBOOK_COOKIE "#{cookie}"
|
||||
ENV LIVEBOOK_CLUSTER "fly"
|
||||
"""
|
||||
|
||||
"dns" ->
|
||||
"""
|
||||
# --- Clustering ---
|
||||
|
||||
# Set the same Livebook secrets across all nodes
|
||||
ENV LIVEBOOK_SECRET_KEY_BASE "#{secret_key_base}"
|
||||
ENV LIVEBOOK_COOKIE "#{cookie}"
|
||||
ENV LIVEBOOK_CLUSTER "dns:QUERY"
|
||||
ENV LIVEBOOK_NODE "livebook_server@MACHINE_IP"
|
||||
"""
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
|
||||
[
|
||||
|
@ -324,17 +339,27 @@ defmodule Livebook.Hubs.Dockerfile do
|
|||
[]
|
||||
end
|
||||
|
||||
clustering_env =
|
||||
if config.clustering == :fly_io do
|
||||
{secret_key_base, cookie} = deterministic_skb_and_cookie(hub.teams_key)
|
||||
{secret_key_base, cookie} = deterministic_skb_and_cookie(hub.teams_key)
|
||||
|
||||
[
|
||||
{"LIVEBOOK_CLUSTER", "fly"},
|
||||
{"LIVEBOOK_SECRET_KEY_BASE", secret_key_base},
|
||||
{"LIVEBOOK_COOKIE", cookie}
|
||||
]
|
||||
else
|
||||
[]
|
||||
clustering_env =
|
||||
case to_string(config.clustering) do
|
||||
"fly_io" ->
|
||||
[
|
||||
{"LIVEBOOK_CLUSTER", "fly"},
|
||||
{"LIVEBOOK_SECRET_KEY_BASE", secret_key_base},
|
||||
{"LIVEBOOK_COOKIE", cookie}
|
||||
]
|
||||
|
||||
"dns" ->
|
||||
[
|
||||
{"LIVEBOOK_NODE", "livebook_server@MACHINE_IP"},
|
||||
{"LIVEBOOK_CLUSTER", "dns:QUERY"},
|
||||
{"LIVEBOOK_SECRET_KEY_BASE", secret_key_base},
|
||||
{"LIVEBOOK_COOKIE", cookie}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
|
||||
%{image: image, env: base_image.env ++ env ++ hub_env ++ clustering_env}
|
||||
|
|
|
@ -25,7 +25,7 @@ defmodule Livebook.Teams.DeploymentGroup do
|
|||
field :name, :string
|
||||
field :mode, Ecto.Enum, values: [:online, :offline], default: :online
|
||||
field :hub_id, :string
|
||||
field :clustering, Ecto.Enum, values: [:fly_io]
|
||||
field :clustering, Ecto.Enum, values: [:fly_io, :dns]
|
||||
field :zta_provider, Ecto.Enum, values: @zta_providers
|
||||
field :zta_key, :string
|
||||
|
||||
|
|
|
@ -92,28 +92,43 @@ defmodule LivebookWeb.AppComponents do
|
|||
def deployment_group_form_content(assigns) do
|
||||
~H"""
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<.select_field
|
||||
label="Clustering"
|
||||
help={
|
||||
~S'''
|
||||
When running multiple
|
||||
instances of Livebook,
|
||||
they need to be connected
|
||||
into a single cluster.
|
||||
You must either deploy
|
||||
it as a single instance
|
||||
or choose a platform to
|
||||
enable clustering on.
|
||||
'''
|
||||
}
|
||||
field={@form[:clustering]}
|
||||
options={[
|
||||
{"Single instance", ""},
|
||||
{"Fly.io", "fly_io"}
|
||||
]}
|
||||
disabled={@disabled}
|
||||
/>
|
||||
<div class="flex flex-col">
|
||||
<.select_field
|
||||
label="Clustering"
|
||||
help={
|
||||
~S'''
|
||||
When running multiple
|
||||
instances of Livebook,
|
||||
they need to be connected
|
||||
into a single cluster.
|
||||
You must either deploy
|
||||
it as a single instance
|
||||
or choose a platform to
|
||||
enable clustering on.
|
||||
'''
|
||||
}
|
||||
field={@form[:clustering]}
|
||||
options={[
|
||||
{"Single instance", ""},
|
||||
{"Fly.io", "fly_io"},
|
||||
{"DNS", "dns"}
|
||||
]}
|
||||
disabled={@disabled}
|
||||
/>
|
||||
|
||||
<div :if={to_string(@form[:clustering].value) == "dns"} class="text-sm mt-1">
|
||||
See the
|
||||
<a
|
||||
class="text-blue-800 hover:text-blue-600"
|
||||
href="https://hexdocs.pm/livebook/docker.html#clustering"
|
||||
>
|
||||
Clustering docs
|
||||
</a>
|
||||
for more information.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if Hubs.Provider.type(@hub) == "team" do %>
|
||||
<div class="flex flex-col">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
|
@ -131,6 +146,7 @@ defmodule LivebookWeb.AppComponents do
|
|||
options={zta_options()}
|
||||
disabled={@disabled}
|
||||
/>
|
||||
|
||||
<.text_field
|
||||
:if={zta_metadata = zta_metadata(@form[:zta_provider].value)}
|
||||
field={@form[:zta_key]}
|
||||
|
@ -141,6 +157,7 @@ defmodule LivebookWeb.AppComponents do
|
|||
disabled={@disabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div :if={zta_metadata = zta_metadata(@form[:zta_provider].value)} class="text-sm mt-1">
|
||||
See the
|
||||
<a
|
||||
|
|
|
@ -207,6 +207,17 @@ defmodule Livebook.Hubs.DockerfileTest do
|
|||
|
||||
assert dockerfile =~ ~s/ENV LIVEBOOK_CLUSTER "fly"/
|
||||
end
|
||||
|
||||
test "deploying with dns cluster setup" do
|
||||
config = dockerfile_config(%{clustering: :dns})
|
||||
hub = personal_hub()
|
||||
file = Livebook.FileSystem.File.local(p("/notebook.livemd"))
|
||||
|
||||
dockerfile = Dockerfile.airgapped_dockerfile(config, hub, [], [], file, [], %{})
|
||||
|
||||
assert dockerfile =~ ~s/ENV LIVEBOOK_NODE "livebook_server@MACHINE_IP"/
|
||||
assert dockerfile =~ ~s/ENV LIVEBOOK_CLUSTER "dns:QUERY"/
|
||||
end
|
||||
end
|
||||
|
||||
describe "online_docker_info/3" do
|
||||
|
@ -255,6 +266,17 @@ defmodule Livebook.Hubs.DockerfileTest do
|
|||
|
||||
assert {"LIVEBOOK_CLUSTER", "fly"} in env
|
||||
end
|
||||
|
||||
test "deploying with dns cluster setup" do
|
||||
config = dockerfile_config(%{clustering: :dns})
|
||||
hub = team_hub()
|
||||
agent_key = Livebook.Factory.build(:agent_key)
|
||||
|
||||
%{env: env} = Dockerfile.online_docker_info(config, hub, agent_key)
|
||||
|
||||
assert {"LIVEBOOK_NODE", "livebook_server@MACHINE_IP"} in env
|
||||
assert {"LIVEBOOK_CLUSTER", "dns:QUERY"} in env
|
||||
end
|
||||
end
|
||||
|
||||
describe "warnings/6" do
|
||||
|
|
|
@ -78,7 +78,7 @@ defmodule Livebook.Teams.ConnectionTest do
|
|||
assert_receive :connected
|
||||
|
||||
# creates a new deployment group with offline mode
|
||||
deployment_group = build(:deployment_group, name: "FOO", mode: :offline)
|
||||
deployment_group = build(:deployment_group, name: "FOO", mode: :offline, clustering: :dns)
|
||||
|
||||
assert {:ok, _id} =
|
||||
Livebook.Teams.create_deployment_group(hub, deployment_group)
|
||||
|
@ -88,11 +88,14 @@ defmodule Livebook.Teams.ConnectionTest do
|
|||
assert deployment_group_created.name == deployment_group.name
|
||||
assert String.to_existing_atom(deployment_group_created.mode) == deployment_group.mode
|
||||
|
||||
assert String.to_existing_atom(deployment_group_created.clustering) ==
|
||||
deployment_group.clustering
|
||||
|
||||
# since the deployment group is with offline mode, the agent key shouldn't exists
|
||||
assert deployment_group_created.agent_keys == []
|
||||
|
||||
# creates a new deployment group with online mode
|
||||
deployment_group = build(:deployment_group, name: "BAR", mode: :online)
|
||||
deployment_group = build(:deployment_group, name: "BAR", mode: :online, clustering: :dns)
|
||||
{:ok, _id} = Livebook.Teams.create_deployment_group(hub, deployment_group)
|
||||
|
||||
# deployment_group name and mode are not encrypted
|
||||
|
@ -100,6 +103,9 @@ defmodule Livebook.Teams.ConnectionTest do
|
|||
assert deployment_group_created.name == deployment_group.name
|
||||
assert String.to_existing_atom(deployment_group_created.mode) == deployment_group.mode
|
||||
|
||||
assert String.to_existing_atom(deployment_group_created.clustering) ==
|
||||
deployment_group.clustering
|
||||
|
||||
# receives the built-in agent key
|
||||
assert [agent_key] = deployment_group_created.agent_keys
|
||||
assert is_binary(agent_key.key)
|
||||
|
|
Loading…
Add table
Reference in a new issue