Files
2026-07-13 13:17:40 +08:00

149 lines
5.2 KiB
Python

import copy
import logging
import os
from cryptography.hazmat.primitives import serialization as crypto_serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from ray.autoscaler._private.constants import DISABLE_NODE_UPDATERS_KEY
from ray.autoscaler._private.event_system import CreateClusterEvent, global_event_system
from ray.autoscaler._private.util import check_legacy_fields
PRIVATE_KEY_NAME = "ray-bootstrap-key.pem"
PUBLIC_KEY_NAME = "ray_bootstrap_public_key.key"
PRIVATE_KEY_PATH = os.path.expanduser(f"~/{PRIVATE_KEY_NAME}")
PUBLIC_KEY_PATH = os.path.expanduser(f"~/{PUBLIC_KEY_NAME}")
logger = logging.getLogger(__name__)
def bootstrap_vsphere(config):
# create a copy of the input config to modify
config = copy.deepcopy(config)
# Log warnings if user included deprecated `head_node` or `worker_nodes`
# fields. Raise error if no `available_node_types`
check_legacy_fields(config)
# Configure SSH access, using an existing key pair if possible.
config = configure_key_pair(config)
# Configure docker run command to be executed on head and wroker nodes
config = configure_run_options(config)
global_event_system.execute_callback(
CreateClusterEvent.ssh_keypair_downloaded,
{"ssh_key_path": config["auth"]["ssh_private_key"]},
)
logger.info(f"{config}")
return config
def configure_key_pair(config):
logger.info("Configuring keys for Ray Cluster Launcher to ssh into the head node.")
if not os.path.exists(PRIVATE_KEY_PATH):
logger.warning(
"Private key file at path {} was not found".format(PRIVATE_KEY_PATH)
)
_create_ssh_keys()
logger.info(
f"New SSH key pair {PRIVATE_KEY_PATH} and {PUBLIC_KEY_PATH} created."
)
# updater.py file uses the following config to ssh onto the head node
# Also, copies the file onto the head node
config["auth"]["ssh_private_key"] = PRIVATE_KEY_PATH
# The path where the public key should be copied onto the remote host
public_key_remote_path = f"~/{PUBLIC_KEY_NAME}"
# Copy the public key to the remote host
config["file_mounts"][public_key_remote_path] = PUBLIC_KEY_PATH
return config
def configure_run_options(config):
ssh_user = config["auth"]["ssh_user"]
# By default enable TLS for Head-Worker grpc communication
tls_enable = (
1 if config["provider"]["vsphere_config"].get("tls_enable", True) else 0
)
# Configure common run options
if "run_options" not in config["docker"]:
config["docker"]["run_options"] = []
config["docker"]["run_options"].append(f"--env RAY_USE_TLS={tls_enable}")
# Configure head_run_options
if "head_run_options" not in config["docker"]:
config["docker"]["head_run_options"] = []
config["docker"]["head_run_options"].append(
f"--env-file /home/{ssh_user}/svc-account-token.env"
)
# Configure worker_run_options
if "worker_run_options" not in config["docker"]:
config["docker"]["worker_run_options"] = []
if tls_enable == 1:
# Generate TLS cert and key for head and worker nodes.
# This needs to be done before ray start command
config["head_start_ray_commands"].insert(0, "sh /home/ray/gencert.sh")
config["worker_start_ray_commands"].insert(0, "sh /home/ray/gencert.sh")
config["docker"]["run_options"].append(
f"-v /home/{ssh_user}/ca.crt:/home/ray/ca.crt"
)
config["docker"]["run_options"].append(
f"-v /home/{ssh_user}/ca.key:/home/ray/ca.key"
)
config["docker"]["run_options"].append(
f"-v /home/{ssh_user}/gencert.sh:/home/ray/gencert.sh"
)
config["docker"]["run_options"].append("--env RAY_TLS_CA_CERT=/home/ray/ca.crt")
config["docker"]["run_options"].append(
"--env RAY_TLS_SERVER_KEY=/home/ray/tls.key"
)
config["docker"]["run_options"].append(
"--env RAY_TLS_SERVER_CERT=/home/ray/tls.crt"
)
return config
def disable_node_updater(config):
logger.info(
"Disabling NodeUpdater threads as Cluster Operator is "
+ "responsible for Ray setup on nodes."
)
config["provider"][DISABLE_NODE_UPDATERS_KEY] = True
return config
def _create_ssh_keys():
"""Create SSH keys as specified"""
# Create a private key
private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
# Encode it in PEM format
unencrypted_private_key = private_key.private_bytes(
encoding=crypto_serialization.Encoding.PEM,
format=crypto_serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=crypto_serialization.NoEncryption(),
)
# Create a public key
public_key = private_key.public_key().public_bytes(
encoding=crypto_serialization.Encoding.PEM,
format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo,
)
# Write keys
with open(PRIVATE_KEY_PATH, "wb") as pvt_key:
# manage access mode for the pvt key
os.chmod(PRIVATE_KEY_PATH, 0o600)
pvt_key.write(unencrypted_private_key)
with open(PUBLIC_KEY_PATH, "wb") as pub_key:
# manage access mode for the pvt key
pub_key.write(public_key)