mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-05 04:24:21 +08:00
Instructions for k8s agent (#2697)
This commit is contained in:
parent
463b14fbf3
commit
de22485641
8 changed files with 259 additions and 49 deletions
|
@ -9,6 +9,7 @@ import { json } from "@codemirror/lang-json";
|
|||
import { xml } from "@codemirror/lang-xml";
|
||||
import { css } from "@codemirror/lang-css";
|
||||
import { html } from "@codemirror/lang-html";
|
||||
import { yaml } from "@codemirror/lang-yaml";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { erlang } from "@codemirror/legacy-modes/mode/erlang";
|
||||
import { dockerFile } from "@codemirror/legacy-modes/mode/dockerfile";
|
||||
|
@ -29,6 +30,11 @@ const sqlDesc = LanguageDescription.of({
|
|||
support: sql(),
|
||||
});
|
||||
|
||||
const yamlDesc = LanguageDescription.of({
|
||||
name: "YAML",
|
||||
support: yaml(),
|
||||
});
|
||||
|
||||
const jsonDesc = LanguageDescription.of({
|
||||
name: "JSON",
|
||||
support: json(),
|
||||
|
@ -67,6 +73,7 @@ const markdownDesc = LanguageDescription.of({
|
|||
elixirDesc,
|
||||
erlangDesc,
|
||||
sqlDesc,
|
||||
yamlDesc,
|
||||
jsonDesc,
|
||||
xmlDesc,
|
||||
cssDesc,
|
||||
|
@ -81,6 +88,7 @@ export const languages = [
|
|||
elixirDesc,
|
||||
erlangDesc,
|
||||
sqlDesc,
|
||||
yamlDesc,
|
||||
jsonDesc,
|
||||
xmlDesc,
|
||||
cssDesc,
|
||||
|
|
59
assets/package-lock.json
generated
59
assets/package-lock.json
generated
|
@ -14,6 +14,7 @@
|
|||
"@codemirror/lang-markdown": "^6.2.3",
|
||||
"@codemirror/lang-sql": "^6.5.5",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/lang-yaml": "^6.1.1",
|
||||
"@codemirror/language": "^6.10.0",
|
||||
"@codemirror/legacy-modes": "^6.3.3",
|
||||
"@codemirror/lint": "^6.4.2",
|
||||
|
@ -1909,6 +1910,19 @@
|
|||
"@lezer/xml": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-yaml": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.1.tgz",
|
||||
"integrity": "sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@lezer/yaml": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
|
||||
|
@ -3160,9 +3174,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz",
|
||||
"integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz",
|
||||
"integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
|
@ -3186,6 +3200,16 @@
|
|||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/yaml": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.3.tgz",
|
||||
"integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
@ -16181,6 +16205,19 @@
|
|||
"@lezer/xml": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@codemirror/lang-yaml": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.1.tgz",
|
||||
"integrity": "sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==",
|
||||
"requires": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@lezer/yaml": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@codemirror/language": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
|
||||
|
@ -17026,9 +17063,9 @@
|
|||
}
|
||||
},
|
||||
"@lezer/lr": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz",
|
||||
"integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz",
|
||||
"integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==",
|
||||
"requires": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
|
@ -17052,6 +17089,16 @@
|
|||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@lezer/yaml": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.3.tgz",
|
||||
"integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==",
|
||||
"requires": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"@codemirror/lang-markdown": "^6.2.3",
|
||||
"@codemirror/lang-sql": "^6.5.5",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/lang-yaml": "^6.1.1",
|
||||
"@codemirror/language": "^6.10.0",
|
||||
"@codemirror/legacy-modes": "^6.3.3",
|
||||
"@codemirror/lint": "^6.4.2",
|
||||
|
|
|
@ -8,7 +8,7 @@ You may set `LIVEBOOK_CLUSTER` to one of the following values.
|
|||
|
||||
## `auto`
|
||||
|
||||
Detects the hosting platform and automatically sets up a cluster using DNS configuration. Currently the only supported platform is Fly.io.
|
||||
Detects the hosting platform and automatically sets up a cluster using DNS configuration. Currently the only supported platform is Fly.io (and Kubernetes when using Livebook Teams).
|
||||
|
||||
## `dns:QUERY`
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ defmodule LivebookWeb.AppComponents do
|
|||
for more information.
|
||||
</div>
|
||||
<p class="mt-1 text-sm">
|
||||
Automatic clustering is available when deploying to Fly.io.
|
||||
Automatic clustering is available when deploying to Fly.io and Kubernetes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -521,6 +521,47 @@ defmodule LivebookWeb.CoreComponents do
|
|||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a highlighted code snippet with a title and a copy button.
|
||||
|
||||
## Examples
|
||||
|
||||
<.code_preview_with_title_and_copy
|
||||
title="
|
||||
source_id="my-snippet"
|
||||
language="elixir"
|
||||
source="System.version()" />
|
||||
|
||||
"""
|
||||
attr :title, :string, required: true
|
||||
attr :source_id, :string, required: true
|
||||
attr :language, :string, required: true
|
||||
attr :source, :string, required: true
|
||||
|
||||
def code_preview_with_title_and_copy(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-700 font-semibold"><%= @title %></span>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<span class="tooltip left" data-tooltip="Copy source">
|
||||
<.icon_button
|
||||
aria-label="copy source"
|
||||
phx-click={JS.dispatch("lb:clipcopy", to: "##{@source_id}")}
|
||||
>
|
||||
<.remix_icon icon="clipboard-line" />
|
||||
</.icon_button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="markdown">
|
||||
<.code_preview source_id={@source_id} language={@language} source={@source} />
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders text with a tiny label.
|
||||
|
||||
|
|
|
@ -38,26 +38,13 @@ defmodule LivebookWeb.AppSessionLive.SourceComponent do
|
|||
<p class="text-gray-700">
|
||||
This app is built from the following notebook source:
|
||||
</p>
|
||||
<div class="flex flex-col space-y-1">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-700 font-semibold">
|
||||
<%= Session.file_name_for_download(@session) <> ".livemd" %>
|
||||
</span>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<span class="tooltip left" data-tooltip="Copy source">
|
||||
<.icon_button
|
||||
aria-label="copy source"
|
||||
phx-click={JS.dispatch("lb:clipcopy", to: "#export-notebook-source")}
|
||||
>
|
||||
<.remix_icon icon="clipboard-line" />
|
||||
</.icon_button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown">
|
||||
<.code_preview source_id="export-notebook-source" language="markdown" source={@source} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<.code_preview_with_title_and_copy
|
||||
title={Session.file_name_for_download(@session) <> ".livemd"}
|
||||
source_id="export-notebook-source"
|
||||
language="markdown"
|
||||
source={@source}
|
||||
/>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
|
|
@ -95,17 +95,13 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupAgentComponent do
|
|||
to set the environment variables as secrets, if applicable. Below is
|
||||
an example calling Docker CLI directly, adapt it as necessary.
|
||||
</p>
|
||||
<div>
|
||||
<div class="flex items-end mb-1 gap-1">
|
||||
<span class="text-sm text-gray-700 font-semibold">CLI</span>
|
||||
</div>
|
||||
|
||||
<.code_preview
|
||||
source_id="agent-dockerfile-source"
|
||||
source={@instructions.docker_instructions}
|
||||
language="shell"
|
||||
/>
|
||||
</div>
|
||||
<.code_preview_with_title_and_copy
|
||||
title="CLI"
|
||||
source_id="agent-dockerfile-source"
|
||||
source={@instructions.docker_instructions}
|
||||
language="shell"
|
||||
/>
|
||||
</div>
|
||||
</:tab>
|
||||
<:tab id="fly_io" label="Fly.io">
|
||||
|
@ -113,17 +109,38 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupAgentComponent do
|
|||
<p class="text-gray-700">
|
||||
Deploy an app server to Fly.io with a few commands.
|
||||
</p>
|
||||
<div>
|
||||
<div class="flex items-end mb-1 gap-1">
|
||||
<span class="text-sm text-gray-700 font-semibold">CLI</span>
|
||||
</div>
|
||||
|
||||
<.code_preview
|
||||
source_id="agent-dockerfile-source"
|
||||
source={@instructions.fly_instructions}
|
||||
language="shell"
|
||||
/>
|
||||
</div>
|
||||
<.code_preview_with_title_and_copy
|
||||
title="CLI"
|
||||
source_id="agent-fly-source"
|
||||
source={@instructions.fly_instructions}
|
||||
language="shell"
|
||||
/>
|
||||
</div>
|
||||
</:tab>
|
||||
<:tab id="k8s" label="Kubernetes">
|
||||
<div class="flex flex-col gap-3">
|
||||
<p class="text-gray-700">
|
||||
Deploy an app server to Kubernetes. First save the following Kubernetes resource file to disk:
|
||||
</p>
|
||||
|
||||
<.code_preview_with_title_and_copy
|
||||
title="livebook.yml"
|
||||
source_id="agent-k8s-source"
|
||||
source={@instructions.k8s_instructions}
|
||||
language="yaml"
|
||||
/>
|
||||
|
||||
<p class="text-gray-700">
|
||||
Now run the following shell command:
|
||||
</p>
|
||||
|
||||
<.code_preview_with_title_and_copy
|
||||
title="CLI"
|
||||
source_id="agent-k8s-cli"
|
||||
source="kubectl apply -f livebook.yml"
|
||||
language="shell"
|
||||
/>
|
||||
</div>
|
||||
</:tab>
|
||||
</.tabs>
|
||||
|
@ -178,7 +195,8 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupAgentComponent do
|
|||
|
||||
%{
|
||||
docker_instructions: docker_instructions(image, env),
|
||||
fly_instructions: fly_instructions(image, env, hub.hub_name, deployment_group.name)
|
||||
fly_instructions: fly_instructions(image, env, hub.hub_name, deployment_group.name),
|
||||
k8s_instructions: k8s_instructions(image, env)
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -213,4 +231,112 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupAgentComponent do
|
|||
fly deploy --ha=false
|
||||
"""
|
||||
end
|
||||
|
||||
defp k8s_instructions(image, env) do
|
||||
{secrets, envs} =
|
||||
Map.split(
|
||||
Map.new(env),
|
||||
~w(LIVEBOOK_TEAMS_KEY LIVEBOOK_TEAMS_AUTH LIVEBOOK_SECRET_KEY_BASE LIVEBOOK_COOKIE)
|
||||
)
|
||||
|
||||
# We replace auto by the cluster setting.
|
||||
{replicas, envs} =
|
||||
case envs do
|
||||
%{"LIVEBOOK_CLUSTER" => "auto"} -> {2, Map.delete(envs, "LIVEBOOK_CLUSTER")}
|
||||
%{} -> {1, envs}
|
||||
end
|
||||
|
||||
envs =
|
||||
Map.put_new(
|
||||
envs,
|
||||
"LIVEBOOK_CLUSTER",
|
||||
"dns:livebook-headless.$(POD_NAMESPACE).svc.cluster.local"
|
||||
)
|
||||
|
||||
k8s_instructions_template(image, envs, secrets, replicas)
|
||||
end
|
||||
|
||||
require EEx
|
||||
|
||||
EEx.function_from_string(
|
||||
:defp,
|
||||
:k8s_instructions_template,
|
||||
"""
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: livebook-headless
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
app: livebook
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: livebook-loadbalancer
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: livebook
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: livebook
|
||||
spec:
|
||||
replicas: <%= replicas %>
|
||||
selector:
|
||||
matchLabels:
|
||||
app: livebook
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: livebook
|
||||
spec:
|
||||
containers:
|
||||
- name: livebook
|
||||
image: <%= image %>
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: LIVEBOOK_NODE
|
||||
value: "livebook@$(POD_IP)"<%= for {k, v} <- envs do %>
|
||||
- name: <%= k %>
|
||||
value: <%= inspect(v) %><% end %><%= for {k, _} <- secrets do %>
|
||||
- name: <%= k %>
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: livebook-secret
|
||||
key: <%= k %><% end %>
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: livebook-secret
|
||||
namespace: livebook-namespace
|
||||
type: Opaque
|
||||
data:
|
||||
# LIVEBOOK_PASSWORD: <base64_encoded_password><%= for {k, v} <- secrets do %>
|
||||
<%= k %>: <%= Base.encode64(v) %><% end %>
|
||||
""",
|
||||
[:image, :envs, :secrets, :replicas]
|
||||
)
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue