Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions docs/docs/concepts/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -1049,27 +1049,27 @@ In case of a self-managed cluster, also specify the IP address of any node in th
- type: kubernetes
kubeconfig:
filename: ~/.kube/config
networking:
ssh_host: localhost # The external IP address of any node
ssh_port: 32000 # Any port accessible outside of the cluster
proxy_jump:
hostname: localhost # The external IP address of any node
port: 32000 # Any port accessible outside of the cluster
```

</div>

The port specified to `ssh_port` must be accessible outside of the cluster.
The port specified to `port` must be accessible outside of the cluster.

??? info "Kind"
If you are using [Kind](https://kind.sigs.k8s.io/), make sure to make
to set up `ssh_port` via `extraPortMappings` for proxying SSH traffic:
to set up `port` via `extraPortMappings` for proxying SSH traffic:

```yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 32000 # Must be same as `ssh_port`
hostPort: 32000 # Must be same as `ssh_port`
- containerPort: 32000 # Must be same as `port`
hostPort: 32000 # Must be same as `port`
```

Go ahead and create the cluster like this:
Expand All @@ -1092,13 +1092,13 @@ In case of a self-managed cluster, also specify the IP address of any node in th
- type: kubernetes
kubeconfig:
filename: ~/.kube/config
networking:
ssh_port: 32000 # Any port accessible outside of the cluster
proxy_jump:
port: 32000 # Any port accessible outside of the cluster
```

</div>

The port specified to `ssh_port` must be accessible outside of the cluster.
The port specified to `port` must be accessible outside of the cluster.

??? info "EKS"
For example, if you are using EKS, make sure to add it via an ingress rule
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/server/config.yml.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
cat my-service-account-file.json | jq -c | jq -R
```

###### `projects[n].backends[type=kubernetes].networking` { #kubernetes-networking data-toc-label="networking" }
###### `projects[n].backends[type=kubernetes].proxy_jump` { #kubernetes-proxy_jump data-toc-label="proxy_jump" }

#SCHEMA# dstack._internal.core.backends.kubernetes.models.KubernetesNetworkingConfig
#SCHEMA# dstack._internal.core.backends.kubernetes.models.KubernetesProxyJumpConfig
overrides:
show_root_heading: false

Expand Down
18 changes: 9 additions & 9 deletions src/dstack/_internal/core/backends/kubernetes/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from dstack._internal.core.backends.base.offers import filter_offers_by_requirements
from dstack._internal.core.backends.kubernetes.models import (
KubernetesConfig,
KubernetesNetworkingConfig,
KubernetesProxyJumpConfig,
)
from dstack._internal.core.backends.kubernetes.utils import (
call_api_method,
Expand Down Expand Up @@ -69,10 +69,10 @@ class KubernetesCompute(
def __init__(self, config: KubernetesConfig):
super().__init__()
self.config = config.copy()
networking_config = self.config.networking
if networking_config is None:
networking_config = KubernetesNetworkingConfig()
self.networking_config = networking_config
proxy_jump = self.config.proxy_jump
if proxy_jump is None:
proxy_jump = KubernetesProxyJumpConfig()
self.proxy_jump = proxy_jump
self.api = get_api_from_config_data(config.kubeconfig.data)

def get_offers_by_requirements(
Expand Down Expand Up @@ -143,7 +143,7 @@ def run_job(
# as an ssh proxy jump to connect to all other services in Kubernetes.
# Setup jump pod in a separate thread to avoid long-running run_job.
# In case the thread fails, the job will be failed and resubmitted.
jump_pod_hostname = self.networking_config.ssh_host
jump_pod_hostname = self.proxy_jump.hostname
if jump_pod_hostname is None:
jump_pod_hostname = get_cluster_public_ip(self.api)
if jump_pod_hostname is None:
Expand All @@ -156,7 +156,7 @@ def run_job(
namespace=self.config.namespace,
project_name=run.project_name,
ssh_public_keys=[project_ssh_public_key.strip(), run.run_spec.ssh_key_pub.strip()],
jump_pod_port=self.networking_config.ssh_port,
jump_pod_port=self.proxy_jump.port,
)
if not created:
threading.Thread(
Expand Down Expand Up @@ -820,11 +820,11 @@ def _run_ssh_command(hostname: str, port: int, ssh_private_key: str, command: st


def _get_jump_pod_name(project_name: str) -> str:
return f"{project_name}-ssh-jump-pod"
return f"dstack-{project_name}-ssh-jump-pod"


def _get_jump_pod_service_name(project_name: str) -> str:
return f"{project_name}-ssh-jump-pod-service"
return f"dstack-{project_name}-ssh-jump-pod-service"


def _get_pod_service_name(pod_name: str) -> str:
Expand Down
12 changes: 6 additions & 6 deletions src/dstack/_internal/core/backends/kubernetes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
DEFAULT_NAMESPACE = "default"


class KubernetesNetworkingConfig(CoreModel):
ssh_host: Annotated[
Optional[str], Field(description="The external IP address of any node")
class KubernetesProxyJumpConfig(CoreModel):
hostname: Annotated[
Optional[str], Field(description="The external IP address or hostname of any node")
] = None
ssh_port: Annotated[
port: Annotated[
Optional[int], Field(description="Any port accessible outside of the cluster")
] = None

Expand All @@ -24,8 +24,8 @@ class KubeconfigConfig(CoreModel):

class KubernetesBackendConfig(CoreModel):
type: Annotated[Literal["kubernetes"], Field(description="The type of backend")] = "kubernetes"
networking: Annotated[
Optional[KubernetesNetworkingConfig], Field(description="The networking configuration")
proxy_jump: Annotated[
Optional[KubernetesProxyJumpConfig], Field(description="The SSH proxy jump configuration")
] = None
namespace: Annotated[
str, Field(description="The namespace for resources managed by `dstack`")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dstack._internal.core.backends.kubernetes.models import (
KubeconfigConfig,
KubernetesBackendConfigWithCreds,
KubernetesNetworkingConfig,
KubernetesProxyJumpConfig,
)
from dstack._internal.core.errors import BackendInvalidCredentialsError

Expand All @@ -17,7 +17,7 @@ class TestKubernetesConfigurator:
def test_validate_config_valid(self):
config = KubernetesBackendConfigWithCreds(
kubeconfig=KubeconfigConfig(data="valid", filename="-"),
networking=KubernetesNetworkingConfig(ssh_host=None, ssh_port=None),
proxy_jump=KubernetesProxyJumpConfig(hostname=None, port=None),
)
with patch(
"dstack._internal.core.backends.kubernetes.utils.get_api_from_config_data"
Expand All @@ -30,7 +30,7 @@ def test_validate_config_valid(self):
def test_validate_config_invalid_config(self):
config = KubernetesBackendConfigWithCreds(
kubeconfig=KubeconfigConfig(data="invalid", filename="-"),
networking=KubernetesNetworkingConfig(ssh_host=None, ssh_port=None),
proxy_jump=KubernetesProxyJumpConfig(hostname=None, port=None),
)
with (
patch(
Expand Down