mirror of
https://github.com/wassname/ray.git
synced 2026-07-27 11:26:41 +08:00
Lint Python files with Yapf (#1872)
This commit is contained in:
committed by
Robert Nishihara
parent
a3ddde398c
commit
74162d1492
@@ -51,25 +51,32 @@ CLUSTER_CONFIG_SCHEMA = {
|
||||
"idle_timeout_minutes": (int, OPTIONAL),
|
||||
|
||||
# Cloud-provider specific configuration.
|
||||
"provider": ({
|
||||
"type": (str, REQUIRED), # e.g. aws
|
||||
"region": (str, OPTIONAL), # e.g. us-east-1
|
||||
"availability_zone": (str, OPTIONAL), # e.g. us-east-1a
|
||||
"module": (str, OPTIONAL), # module, if using external node provider
|
||||
}, REQUIRED),
|
||||
"provider": (
|
||||
{
|
||||
"type": (str, REQUIRED), # e.g. aws
|
||||
"region": (str, OPTIONAL), # e.g. us-east-1
|
||||
"availability_zone": (str, OPTIONAL), # e.g. us-east-1a
|
||||
"module": (str,
|
||||
OPTIONAL), # module, if using external node provider
|
||||
},
|
||||
REQUIRED),
|
||||
|
||||
# How Ray will authenticate with newly launched nodes.
|
||||
"auth": ({
|
||||
"ssh_user": (str, REQUIRED), # e.g. ubuntu
|
||||
"ssh_private_key": (str, OPTIONAL),
|
||||
}, REQUIRED),
|
||||
"auth": (
|
||||
{
|
||||
"ssh_user": (str, REQUIRED), # e.g. ubuntu
|
||||
"ssh_private_key": (str, OPTIONAL),
|
||||
},
|
||||
REQUIRED),
|
||||
|
||||
# Docker configuration. If this is specified, all setup and start commands
|
||||
# will be executed in the container.
|
||||
"docker": ({
|
||||
"image": (str, OPTIONAL), # e.g. tensorflow/tensorflow:1.5.0-py3
|
||||
"container_name": (str, OPTIONAL), # e.g., ray_docker
|
||||
}, OPTIONAL),
|
||||
"docker": (
|
||||
{
|
||||
"image": (str, OPTIONAL), # e.g. tensorflow/tensorflow:1.5.0-py3
|
||||
"container_name": (str, OPTIONAL), # e.g., ray_docker
|
||||
},
|
||||
OPTIONAL),
|
||||
|
||||
# Provider-specific config for the head node, e.g. instance type.
|
||||
"head_node": (dict, OPTIONAL),
|
||||
@@ -137,9 +144,9 @@ class LoadMetrics(object):
|
||||
for unwanted_key in unwanted:
|
||||
del mapping[unwanted_key]
|
||||
if unwanted:
|
||||
print(
|
||||
"Removed {} stale ip mappings: {} not in {}".format(
|
||||
len(unwanted), unwanted, active_ips))
|
||||
print("Removed {} stale ip mappings: {} not in {}".format(
|
||||
len(unwanted), unwanted, active_ips))
|
||||
|
||||
prune(self.last_used_time_by_ip)
|
||||
prune(self.static_resources_by_ip)
|
||||
prune(self.dynamic_resources_by_ip)
|
||||
@@ -148,10 +155,8 @@ class LoadMetrics(object):
|
||||
return self._info()["NumNodesUsed"]
|
||||
|
||||
def debug_string(self):
|
||||
return " - {}".format(
|
||||
"\n - ".join(
|
||||
["{}: {}".format(k, v)
|
||||
for k, v in sorted(self._info().items())]))
|
||||
return " - {}".format("\n - ".join(
|
||||
["{}: {}".format(k, v) for k, v in sorted(self._info().items())]))
|
||||
|
||||
def _info(self):
|
||||
nodes_used = 0.0
|
||||
@@ -176,14 +181,19 @@ class LoadMetrics(object):
|
||||
nodes_used += max_frac
|
||||
idle_times = [now - t for t in self.last_used_time_by_ip.values()]
|
||||
return {
|
||||
"ResourceUsage": ", ".join([
|
||||
"ResourceUsage":
|
||||
", ".join([
|
||||
"{}/{} {}".format(
|
||||
round(resources_used[rid], 2),
|
||||
round(resources_total[rid], 2), rid)
|
||||
for rid in sorted(resources_used)]),
|
||||
"NumNodesConnected": len(self.static_resources_by_ip),
|
||||
"NumNodesUsed": round(nodes_used, 2),
|
||||
"NodeIdleSeconds": "Min={} Mean={} Max={}".format(
|
||||
for rid in sorted(resources_used)
|
||||
]),
|
||||
"NumNodesConnected":
|
||||
len(self.static_resources_by_ip),
|
||||
"NumNodesUsed":
|
||||
round(nodes_used, 2),
|
||||
"NodeIdleSeconds":
|
||||
"Min={} Mean={} Max={}".format(
|
||||
int(np.min(idle_times)) if idle_times else -1,
|
||||
int(np.mean(idle_times)) if idle_times else -1,
|
||||
int(np.max(idle_times)) if idle_times else -1),
|
||||
@@ -208,18 +218,20 @@ class StandardAutoscaler(object):
|
||||
until the target cluster size is met).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config_path, load_metrics,
|
||||
max_concurrent_launches=AUTOSCALER_MAX_CONCURRENT_LAUNCHES,
|
||||
max_failures=AUTOSCALER_MAX_NUM_FAILURES,
|
||||
process_runner=subprocess, verbose_updates=False,
|
||||
node_updater_cls=NodeUpdaterProcess,
|
||||
update_interval_s=AUTOSCALER_UPDATE_INTERVAL_S):
|
||||
def __init__(self,
|
||||
config_path,
|
||||
load_metrics,
|
||||
max_concurrent_launches=AUTOSCALER_MAX_CONCURRENT_LAUNCHES,
|
||||
max_failures=AUTOSCALER_MAX_NUM_FAILURES,
|
||||
process_runner=subprocess,
|
||||
verbose_updates=False,
|
||||
node_updater_cls=NodeUpdaterProcess,
|
||||
update_interval_s=AUTOSCALER_UPDATE_INTERVAL_S):
|
||||
self.config_path = config_path
|
||||
self.reload_config(errors_fatal=True)
|
||||
self.load_metrics = load_metrics
|
||||
self.provider = get_node_provider(
|
||||
self.config["provider"], self.config["cluster_name"])
|
||||
self.provider = get_node_provider(self.config["provider"],
|
||||
self.config["cluster_name"])
|
||||
|
||||
self.max_failures = max_failures
|
||||
self.max_concurrent_launches = max_concurrent_launches
|
||||
@@ -245,9 +257,8 @@ class StandardAutoscaler(object):
|
||||
self.reload_config(errors_fatal=False)
|
||||
self._update()
|
||||
except Exception as e:
|
||||
print(
|
||||
"StandardAutoscaler: Error during autoscaling: {}",
|
||||
traceback.format_exc())
|
||||
print("StandardAutoscaler: Error during autoscaling: {}",
|
||||
traceback.format_exc())
|
||||
self.num_failures += 1
|
||||
if self.num_failures > self.max_failures:
|
||||
print("*** StandardAutoscaler: Too many errors, abort. ***")
|
||||
@@ -274,15 +285,13 @@ class StandardAutoscaler(object):
|
||||
if node_ip in last_used and last_used[node_ip] < horizon and \
|
||||
len(nodes) - num_terminated > self.config["min_workers"]:
|
||||
num_terminated += 1
|
||||
print(
|
||||
"StandardAutoscaler: Terminating idle node: "
|
||||
"{}".format(node_id))
|
||||
print("StandardAutoscaler: Terminating idle node: "
|
||||
"{}".format(node_id))
|
||||
self.provider.terminate_node(node_id)
|
||||
elif not self.launch_config_ok(node_id):
|
||||
num_terminated += 1
|
||||
print(
|
||||
"StandardAutoscaler: Terminating outdated node: "
|
||||
"{}".format(node_id))
|
||||
print("StandardAutoscaler: Terminating outdated node: "
|
||||
"{}".format(node_id))
|
||||
self.provider.terminate_node(node_id)
|
||||
if num_terminated > 0:
|
||||
nodes = self.workers()
|
||||
@@ -292,9 +301,8 @@ class StandardAutoscaler(object):
|
||||
num_terminated = 0
|
||||
while len(nodes) > self.config["max_workers"]:
|
||||
num_terminated += 1
|
||||
print(
|
||||
"StandardAutoscaler: Terminating unneeded node: "
|
||||
"{}".format(nodes[-1]))
|
||||
print("StandardAutoscaler: Terminating unneeded node: "
|
||||
"{}".format(nodes[-1]))
|
||||
self.provider.terminate_node(nodes[-1])
|
||||
nodes = nodes[:-1]
|
||||
if num_terminated > 0:
|
||||
@@ -339,13 +347,13 @@ class StandardAutoscaler(object):
|
||||
with open(self.config_path) as f:
|
||||
new_config = yaml.load(f.read())
|
||||
validate_config(new_config)
|
||||
new_launch_hash = hash_launch_conf(
|
||||
new_config["worker_nodes"], new_config["auth"])
|
||||
new_runtime_hash = hash_runtime_conf(
|
||||
new_config["file_mounts"],
|
||||
[new_config["setup_commands"],
|
||||
new_config["worker_setup_commands"],
|
||||
new_config["worker_start_ray_commands"]])
|
||||
new_launch_hash = hash_launch_conf(new_config["worker_nodes"],
|
||||
new_config["auth"])
|
||||
new_runtime_hash = hash_runtime_conf(new_config["file_mounts"], [
|
||||
new_config["setup_commands"],
|
||||
new_config["worker_setup_commands"],
|
||||
new_config["worker_start_ray_commands"]
|
||||
])
|
||||
self.config = new_config
|
||||
self.launch_hash = new_launch_hash
|
||||
self.runtime_hash = new_runtime_hash
|
||||
@@ -353,17 +361,15 @@ class StandardAutoscaler(object):
|
||||
if errors_fatal:
|
||||
raise e
|
||||
else:
|
||||
print(
|
||||
"StandardAutoscaler: Error parsing config: {}",
|
||||
traceback.format_exc())
|
||||
print("StandardAutoscaler: Error parsing config: {}",
|
||||
traceback.format_exc())
|
||||
|
||||
def target_num_workers(self):
|
||||
target_frac = self.config["target_utilization_fraction"]
|
||||
cur_used = self.load_metrics.approx_workers_used()
|
||||
ideal_num_workers = int(np.ceil(cur_used / float(target_frac)))
|
||||
return min(
|
||||
self.config["max_workers"],
|
||||
max(self.config["min_workers"], ideal_num_workers))
|
||||
return min(self.config["max_workers"],
|
||||
max(self.config["min_workers"], ideal_num_workers))
|
||||
|
||||
def launch_config_ok(self, node_id):
|
||||
launch_conf = self.provider.node_tags(node_id).get(
|
||||
@@ -393,8 +399,7 @@ class StandardAutoscaler(object):
|
||||
node_id,
|
||||
self.config["provider"],
|
||||
self.config["auth"],
|
||||
self.config["cluster_name"],
|
||||
{},
|
||||
self.config["cluster_name"], {},
|
||||
with_head_node_ip(self.config["worker_start_ray_commands"]),
|
||||
self.runtime_hash,
|
||||
redirect_output=not self.verbose_updates,
|
||||
@@ -409,14 +414,12 @@ class StandardAutoscaler(object):
|
||||
return
|
||||
if self.config.get("no_restart", False) and \
|
||||
self.num_successful_updates.get(node_id, 0) > 0:
|
||||
init_commands = (
|
||||
self.config["setup_commands"] +
|
||||
self.config["worker_setup_commands"])
|
||||
init_commands = (self.config["setup_commands"] +
|
||||
self.config["worker_setup_commands"])
|
||||
else:
|
||||
init_commands = (
|
||||
self.config["setup_commands"] +
|
||||
self.config["worker_setup_commands"] +
|
||||
self.config["worker_start_ray_commands"])
|
||||
init_commands = (self.config["setup_commands"] +
|
||||
self.config["worker_setup_commands"] +
|
||||
self.config["worker_start_ray_commands"])
|
||||
updater = self.node_updater_cls(
|
||||
node_id,
|
||||
self.config["provider"],
|
||||
@@ -445,14 +448,12 @@ class StandardAutoscaler(object):
|
||||
print("StandardAutoscaler: Launching {} new nodes".format(count))
|
||||
num_before = len(self.workers())
|
||||
self.provider.create_node(
|
||||
self.config["worker_nodes"],
|
||||
{
|
||||
self.config["worker_nodes"], {
|
||||
TAG_NAME: "ray-{}-worker".format(self.config["cluster_name"]),
|
||||
TAG_RAY_NODE_TYPE: "Worker",
|
||||
TAG_RAY_NODE_STATUS: "Uninitialized",
|
||||
TAG_RAY_LAUNCH_CONFIG: self.launch_hash,
|
||||
},
|
||||
count)
|
||||
}, count)
|
||||
# TODO(ekl) be less conservative in this check
|
||||
assert len(self.workers()) > num_before, \
|
||||
"Num nodes failed to increase after creating a new node"
|
||||
@@ -472,8 +473,8 @@ class StandardAutoscaler(object):
|
||||
suffix += " ({} failed to update)".format(
|
||||
len(self.num_failed_updates))
|
||||
return "StandardAutoscaler [{}]: {}/{} target nodes{}\n{}".format(
|
||||
datetime.now(), len(nodes), self.target_num_workers(),
|
||||
suffix, self.load_metrics.debug_string())
|
||||
datetime.now(), len(nodes), self.target_num_workers(), suffix,
|
||||
self.load_metrics.debug_string())
|
||||
|
||||
|
||||
def typename(v):
|
||||
@@ -507,9 +508,8 @@ def check_extraneous(config, schema):
|
||||
raise ValueError("Config {} is not a dictionary".format(config))
|
||||
for k in config:
|
||||
if k not in schema:
|
||||
raise ValueError(
|
||||
"Unexpected config key `{}` not in {}".format(
|
||||
k, list(schema.keys())))
|
||||
raise ValueError("Unexpected config key `{}` not in {}".format(
|
||||
k, list(schema.keys())))
|
||||
v, kreq = schema[k]
|
||||
if v is None:
|
||||
continue
|
||||
@@ -517,7 +517,8 @@ def check_extraneous(config, schema):
|
||||
if not isinstance(config[k], v):
|
||||
raise ValueError(
|
||||
"Config key `{}` has wrong type {}, expected {}".format(
|
||||
k, type(config[k]).__name__, v.__name__))
|
||||
k,
|
||||
type(config[k]).__name__, v.__name__))
|
||||
else:
|
||||
check_extraneous(config[k], v)
|
||||
|
||||
|
||||
@@ -25,12 +25,10 @@ assert StrictVersion(boto3.__version__) >= StrictVersion("1.4.8"), \
|
||||
def key_pair(i, region):
|
||||
"""Returns the ith default (aws_key_pair_name, key_pair_path)."""
|
||||
if i == 0:
|
||||
return (
|
||||
"{}_{}".format(RAY, region),
|
||||
os.path.expanduser("~/.ssh/{}_{}.pem".format(RAY, region)))
|
||||
return (
|
||||
"{}_{}_{}".format(RAY, i, region),
|
||||
os.path.expanduser("~/.ssh/{}_{}_{}.pem".format(RAY, i, region)))
|
||||
return ("{}_{}".format(RAY, region),
|
||||
os.path.expanduser("~/.ssh/{}_{}.pem".format(RAY, region)))
|
||||
return ("{}_{}_{}".format(RAY, i, region),
|
||||
os.path.expanduser("~/.ssh/{}_{}_{}.pem".format(RAY, i, region)))
|
||||
|
||||
|
||||
# Suppress excessive connection dropped logs from boto
|
||||
@@ -83,7 +81,9 @@ def _configure_iam_role(config):
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {"Service": "ec2.amazonaws.com"},
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole",
|
||||
},
|
||||
],
|
||||
@@ -97,8 +97,7 @@ def _configure_iam_role(config):
|
||||
profile.add_role(RoleName=role.name)
|
||||
time.sleep(15) # wait for propagation
|
||||
|
||||
print("Role not specified for head node, using {}".format(
|
||||
profile.arn))
|
||||
print("Role not specified for head node, using {}".format(profile.arn))
|
||||
config["head_node"]["IamInstanceProfile"] = {"Arn": profile.arn}
|
||||
|
||||
return config
|
||||
@@ -146,8 +145,10 @@ def _configure_key_pair(config):
|
||||
def _configure_subnet(config):
|
||||
ec2 = _resource("ec2", config)
|
||||
subnets = sorted(
|
||||
[s for s in ec2.subnets.all()
|
||||
if s.state == "available" and s.map_public_ip_on_launch],
|
||||
[
|
||||
s for s in ec2.subnets.all()
|
||||
if s.state == "available" and s.map_public_ip_on_launch
|
||||
],
|
||||
reverse=True, # sort from Z-A
|
||||
key=lambda subnet: subnet.availability_zone)
|
||||
if not subnets:
|
||||
@@ -157,9 +158,9 @@ def _configure_subnet(config):
|
||||
"and trying this again. Note that the subnet must map public IPs "
|
||||
"on instance launch.")
|
||||
if "availability_zone" in config["provider"]:
|
||||
default_subnet = next((s for s in subnets
|
||||
if s.availability_zone ==
|
||||
config["provider"]["availability_zone"]),
|
||||
default_subnet = next((
|
||||
s for s in subnets
|
||||
if s.availability_zone == config["provider"]["availability_zone"]),
|
||||
None)
|
||||
if not default_subnet:
|
||||
raise Exception(
|
||||
@@ -209,11 +210,21 @@ def _configure_security_group(config):
|
||||
|
||||
if not security_group.ip_permissions:
|
||||
security_group.authorize_ingress(
|
||||
IpPermissions=[
|
||||
{"FromPort": -1, "ToPort": -1, "IpProtocol": "-1",
|
||||
"UserIdGroupPairs": [{"GroupId": security_group.id}]},
|
||||
{"FromPort": 22, "ToPort": 22, "IpProtocol": "TCP",
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}]}])
|
||||
IpPermissions=[{
|
||||
"FromPort": -1,
|
||||
"ToPort": -1,
|
||||
"IpProtocol": "-1",
|
||||
"UserIdGroupPairs": [{
|
||||
"GroupId": security_group.id
|
||||
}]
|
||||
}, {
|
||||
"FromPort": 22,
|
||||
"ToPort": 22,
|
||||
"IpProtocol": "TCP",
|
||||
"IpRanges": [{
|
||||
"CidrIp": "0.0.0.0/0"
|
||||
}]
|
||||
}])
|
||||
|
||||
if "SecurityGroupIds" not in config["head_node"]:
|
||||
print("SecurityGroupIds not specified for head node, using {}".format(
|
||||
@@ -231,8 +242,10 @@ def _configure_security_group(config):
|
||||
def _get_subnet_or_die(config, subnet_id):
|
||||
ec2 = _resource("ec2", config)
|
||||
subnet = list(
|
||||
ec2.subnets.filter(Filters=[
|
||||
{"Name": "subnet-id", "Values": [subnet_id]}]))
|
||||
ec2.subnets.filter(Filters=[{
|
||||
"Name": "subnet-id",
|
||||
"Values": [subnet_id]
|
||||
}]))
|
||||
assert len(subnet) == 1, "Subnet not found"
|
||||
subnet = subnet[0]
|
||||
return subnet
|
||||
@@ -241,8 +254,10 @@ def _get_subnet_or_die(config, subnet_id):
|
||||
def _get_security_group(config, vpc_id, group_name):
|
||||
ec2 = _resource("ec2", config)
|
||||
existing_groups = list(
|
||||
ec2.security_groups.filter(Filters=[
|
||||
{"Name": "vpc-id", "Values": [vpc_id]}]))
|
||||
ec2.security_groups.filter(Filters=[{
|
||||
"Name": "vpc-id",
|
||||
"Values": [vpc_id]
|
||||
}]))
|
||||
for sg in existing_groups:
|
||||
if sg.group_name == group_name:
|
||||
return sg
|
||||
@@ -270,8 +285,10 @@ def _get_instance_profile(profile_name, config):
|
||||
|
||||
def _get_key(key_name, config):
|
||||
ec2 = _resource("ec2", config)
|
||||
for key in ec2.key_pairs.filter(
|
||||
Filters=[{"Name": "key-name", "Values": [key_name]}]):
|
||||
for key in ec2.key_pairs.filter(Filters=[{
|
||||
"Name": "key-name",
|
||||
"Values": [key_name]
|
||||
}]):
|
||||
if key.name == key_name:
|
||||
return key
|
||||
|
||||
|
||||
@@ -84,7 +84,8 @@ class AWSNodeProvider(NodeProvider):
|
||||
tag_pairs = []
|
||||
for k, v in tags.items():
|
||||
tag_pairs.append({
|
||||
"Key": k, "Value": v,
|
||||
"Key": k,
|
||||
"Value": v,
|
||||
})
|
||||
node.create_tags(Tags=tag_pairs)
|
||||
|
||||
@@ -95,20 +96,20 @@ class AWSNodeProvider(NodeProvider):
|
||||
"Value": self.cluster_name,
|
||||
}]
|
||||
for k, v in tags.items():
|
||||
tag_pairs.append(
|
||||
{
|
||||
"Key": k,
|
||||
"Value": v,
|
||||
})
|
||||
tag_pairs.append({
|
||||
"Key": k,
|
||||
"Value": v,
|
||||
})
|
||||
conf.update({
|
||||
"MinCount": 1,
|
||||
"MaxCount": count,
|
||||
"TagSpecifications": conf.get("TagSpecifications", []) + [
|
||||
{
|
||||
"ResourceType": "instance",
|
||||
"Tags": tag_pairs,
|
||||
}
|
||||
]
|
||||
"MinCount":
|
||||
1,
|
||||
"MaxCount":
|
||||
count,
|
||||
"TagSpecifications":
|
||||
conf.get("TagSpecifications", []) + [{
|
||||
"ResourceType": "instance",
|
||||
"Tags": tag_pairs,
|
||||
}]
|
||||
})
|
||||
self.ec2.create_instances(**conf)
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@ from ray.autoscaler.tags import TAG_RAY_NODE_TYPE, TAG_RAY_LAUNCH_CONFIG, \
|
||||
from ray.autoscaler.updater import NodeUpdaterProcess
|
||||
|
||||
|
||||
def create_or_update_cluster(
|
||||
config_file, override_min_workers, override_max_workers,
|
||||
no_restart, yes):
|
||||
def create_or_update_cluster(config_file, override_min_workers,
|
||||
override_max_workers, no_restart, yes):
|
||||
"""Create or updates an autoscaling Ray cluster from a config json."""
|
||||
|
||||
config = yaml.load(open(config_file).read())
|
||||
@@ -39,8 +38,8 @@ def create_or_update_cluster(
|
||||
|
||||
importer = NODE_PROVIDERS.get(config["provider"]["type"])
|
||||
if not importer:
|
||||
raise NotImplementedError(
|
||||
"Unsupported provider {}".format(config["provider"]))
|
||||
raise NotImplementedError("Unsupported provider {}".format(
|
||||
config["provider"]))
|
||||
|
||||
bootstrap_config, _ = importer()
|
||||
config = bootstrap_config(config)
|
||||
@@ -129,8 +128,10 @@ def get_or_create_head_node(config, no_restart, yes):
|
||||
remote_config_file.write(json.dumps(remote_config))
|
||||
remote_config_file.flush()
|
||||
config["file_mounts"].update({
|
||||
remote_key_path: config["auth"]["ssh_private_key"],
|
||||
"~/ray_bootstrap_config.yaml": remote_config_file.name
|
||||
remote_key_path:
|
||||
config["auth"]["ssh_private_key"],
|
||||
"~/ray_bootstrap_config.yaml":
|
||||
remote_config_file.name
|
||||
})
|
||||
|
||||
if no_restart:
|
||||
@@ -160,30 +161,24 @@ def get_or_create_head_node(config, no_restart, yes):
|
||||
print("Error: updating {} failed".format(
|
||||
provider.external_ip(head_node)))
|
||||
sys.exit(1)
|
||||
print(
|
||||
"Head node up-to-date, IP address is: {}".format(
|
||||
provider.external_ip(head_node)))
|
||||
print("Head node up-to-date, IP address is: {}".format(
|
||||
provider.external_ip(head_node)))
|
||||
|
||||
monitor_str = "tail -f /tmp/raylogs/monitor-*"
|
||||
for s in init_commands:
|
||||
if ("ray start" in s and "docker exec" in s and
|
||||
"--autoscaling-config" in s):
|
||||
if ("ray start" in s and "docker exec" in s
|
||||
and "--autoscaling-config" in s):
|
||||
monitor_str = "docker exec {} /bin/sh -c {}".format(
|
||||
config["docker"]["container_name"],
|
||||
quote(monitor_str))
|
||||
print(
|
||||
"To monitor auto-scaling activity, you can run:\n\n"
|
||||
" ssh -i {} {}@{} {}\n".format(
|
||||
config["auth"]["ssh_private_key"],
|
||||
config["auth"]["ssh_user"],
|
||||
provider.external_ip(head_node),
|
||||
quote(monitor_str)))
|
||||
print(
|
||||
"To login to the cluster, run:\n\n"
|
||||
" ssh -i {} {}@{}\n".format(
|
||||
config["auth"]["ssh_private_key"],
|
||||
config["auth"]["ssh_user"],
|
||||
provider.external_ip(head_node)))
|
||||
config["docker"]["container_name"], quote(monitor_str))
|
||||
print("To monitor auto-scaling activity, you can run:\n\n"
|
||||
" ssh -i {} {}@{} {}\n".format(config["auth"]["ssh_private_key"],
|
||||
config["auth"]["ssh_user"],
|
||||
provider.external_ip(head_node),
|
||||
quote(monitor_str)))
|
||||
print("To login to the cluster, run:\n\n"
|
||||
" ssh -i {} {}@{}\n".format(config["auth"]["ssh_private_key"],
|
||||
config["auth"]["ssh_user"],
|
||||
provider.external_ip(head_node)))
|
||||
|
||||
|
||||
def get_head_node_ip(config_file):
|
||||
|
||||
@@ -22,24 +22,21 @@ def dockerize_if_needed(config):
|
||||
assert cname, "Must provide container name!"
|
||||
docker_mounts = {dst: dst for dst in config["file_mounts"]}
|
||||
config["setup_commands"] = (
|
||||
docker_install_cmds() +
|
||||
docker_start_cmds(
|
||||
config["auth"]["ssh_user"], docker_image,
|
||||
docker_mounts, cname) +
|
||||
with_docker_exec(
|
||||
config["setup_commands"], container_name=cname))
|
||||
docker_install_cmds() + docker_start_cmds(
|
||||
config["auth"]["ssh_user"], docker_image, docker_mounts, cname) +
|
||||
with_docker_exec(config["setup_commands"], container_name=cname))
|
||||
|
||||
config["head_setup_commands"] = with_docker_exec(
|
||||
config["head_setup_commands"], container_name=cname)
|
||||
config["head_start_ray_commands"] = (
|
||||
docker_autoscaler_setup(cname) +
|
||||
with_docker_exec(
|
||||
docker_autoscaler_setup(cname) + with_docker_exec(
|
||||
config["head_start_ray_commands"], container_name=cname))
|
||||
|
||||
config["worker_setup_commands"] = with_docker_exec(
|
||||
config["worker_setup_commands"], container_name=cname)
|
||||
config["worker_start_ray_commands"] = with_docker_exec(
|
||||
config["worker_start_ray_commands"], container_name=cname,
|
||||
config["worker_start_ray_commands"],
|
||||
container_name=cname,
|
||||
env_vars=["RAY_HEAD_IP"])
|
||||
|
||||
return config
|
||||
@@ -50,21 +47,24 @@ def with_docker_exec(cmds, container_name, env_vars=None):
|
||||
if env_vars:
|
||||
env_str = " ".join(
|
||||
["-e {env}=${env}".format(env=env) for env in env_vars])
|
||||
return ["docker exec {} {} /bin/sh -c {} ".format(
|
||||
env_str, container_name, quote(cmd)) for cmd in cmds]
|
||||
return [
|
||||
"docker exec {} {} /bin/sh -c {} ".format(env_str, container_name,
|
||||
quote(cmd)) for cmd in cmds
|
||||
]
|
||||
|
||||
|
||||
def docker_install_cmds():
|
||||
return [aptwait_cmd() + " && sudo apt-get update",
|
||||
aptwait_cmd() + " && sudo apt-get install -y docker.io"]
|
||||
return [
|
||||
aptwait_cmd() + " && sudo apt-get update",
|
||||
aptwait_cmd() + " && sudo apt-get install -y docker.io"
|
||||
]
|
||||
|
||||
|
||||
def aptwait_cmd():
|
||||
return (
|
||||
"while sudo fuser"
|
||||
" /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock"
|
||||
" >/dev/null 2>&1; "
|
||||
"do echo 'Waiting for release of dpkg/apt locks'; sleep 5; done")
|
||||
return ("while sudo fuser"
|
||||
" /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock"
|
||||
" >/dev/null 2>&1; "
|
||||
"do echo 'Waiting for release of dpkg/apt locks'; sleep 5; done")
|
||||
|
||||
|
||||
def docker_start_cmds(user, image, mount, cname):
|
||||
@@ -77,10 +77,12 @@ def docker_start_cmds(user, image, mount, cname):
|
||||
|
||||
# create flags
|
||||
# ports for the redis, object manager, and tune client
|
||||
port_flags = " ".join(["-p {port}:{port}".format(port=port)
|
||||
for port in ["6379", "8076", "4321"]])
|
||||
mount_flags = " ".join(["-v {src}:{dest}".format(src=k, dest=v)
|
||||
for k, v in mount.items()])
|
||||
port_flags = " ".join([
|
||||
"-p {port}:{port}".format(port=port)
|
||||
for port in ["6379", "8076", "4321"]
|
||||
])
|
||||
mount_flags = " ".join(
|
||||
["-v {src}:{dest}".format(src=k, dest=v) for k, v in mount.items()])
|
||||
|
||||
# for click, used in ray cli
|
||||
env_vars = {"LC_ALL": "C.UTF-8", "LANG": "C.UTF-8"}
|
||||
@@ -88,9 +90,10 @@ def docker_start_cmds(user, image, mount, cname):
|
||||
["-e {name}={val}".format(name=k, val=v) for k, v in env_vars.items()])
|
||||
|
||||
# docker run command
|
||||
docker_run = ["docker", "run", "--rm", "--name {}".format(cname),
|
||||
"-d", "-it", port_flags, mount_flags, env_flags,
|
||||
"--net=host", image, "bash"]
|
||||
docker_run = [
|
||||
"docker", "run", "--rm", "--name {}".format(cname), "-d", "-it",
|
||||
port_flags, mount_flags, env_flags, "--net=host", image, "bash"
|
||||
]
|
||||
cmds.append(" ".join(docker_run))
|
||||
docker_update = []
|
||||
docker_update.append("apt-get -y update")
|
||||
@@ -107,7 +110,8 @@ def docker_autoscaler_setup(cname):
|
||||
base_path = os.path.basename(path)
|
||||
cmds.append("docker cp {path} {cname}:{dpath}".format(
|
||||
path=path, dpath=base_path, cname=cname))
|
||||
cmds.extend(with_docker_exec(
|
||||
["cp {} {}".format("/" + base_path, path)],
|
||||
container_name=cname))
|
||||
cmds.extend(
|
||||
with_docker_exec(
|
||||
["cp {} {}".format("/" + base_path, path)],
|
||||
container_name=cname))
|
||||
return cmds
|
||||
|
||||
@@ -15,14 +15,15 @@ def import_aws():
|
||||
|
||||
def load_aws_config():
|
||||
import ray.autoscaler.aws as ray_aws
|
||||
return os.path.join(os.path.dirname(
|
||||
ray_aws.__file__), "example-full.yaml")
|
||||
return os.path.join(os.path.dirname(ray_aws.__file__), "example-full.yaml")
|
||||
|
||||
|
||||
def import_external():
|
||||
"""Mock a normal provider importer."""
|
||||
|
||||
def return_it_back(config):
|
||||
return config
|
||||
|
||||
return return_it_back, None
|
||||
|
||||
|
||||
@@ -55,8 +56,7 @@ def load_class(path):
|
||||
class_data = path.split(".")
|
||||
if len(class_data) < 2:
|
||||
raise ValueError(
|
||||
"You need to pass a valid path like mymodule.provider_class"
|
||||
)
|
||||
"You need to pass a valid path like mymodule.provider_class")
|
||||
module_path = ".".join(class_data[:-1])
|
||||
class_str = class_data[-1]
|
||||
module = importlib.import_module(module_path)
|
||||
@@ -71,8 +71,8 @@ def get_node_provider(provider_config, cluster_name):
|
||||
importer = NODE_PROVIDERS.get(provider_config["type"])
|
||||
|
||||
if importer is None:
|
||||
raise NotImplementedError(
|
||||
"Unsupported node provider: {}".format(provider_config["type"]))
|
||||
raise NotImplementedError("Unsupported node provider: {}".format(
|
||||
provider_config["type"]))
|
||||
_, provider_cls = importer()
|
||||
return provider_cls(provider_config, cluster_name)
|
||||
|
||||
@@ -82,8 +82,8 @@ def get_default_config(provider_config):
|
||||
return {}
|
||||
load_config = DEFAULT_CONFIGS.get(provider_config["type"])
|
||||
if load_config is None:
|
||||
raise NotImplementedError(
|
||||
"Unsupported node provider: {}".format(provider_config["type"]))
|
||||
raise NotImplementedError("Unsupported node provider: {}".format(
|
||||
provider_config["type"]))
|
||||
path_to_default = load_config()
|
||||
with open(path_to_default) as f:
|
||||
defaults = yaml.load(f)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
"""The Ray autoscaler uses tags to associate metadata with instances."""
|
||||
|
||||
# Tag for the name of the node
|
||||
|
||||
@@ -26,10 +26,16 @@ def pretty_cmd(cmd_str):
|
||||
class NodeUpdater(object):
|
||||
"""A process for syncing files and running init commands on a node."""
|
||||
|
||||
def __init__(
|
||||
self, node_id, provider_config, auth_config, cluster_name,
|
||||
file_mounts, setup_cmds, runtime_hash, redirect_output=True,
|
||||
process_runner=subprocess):
|
||||
def __init__(self,
|
||||
node_id,
|
||||
provider_config,
|
||||
auth_config,
|
||||
cluster_name,
|
||||
file_mounts,
|
||||
setup_cmds,
|
||||
runtime_hash,
|
||||
redirect_output=True,
|
||||
process_runner=subprocess):
|
||||
self.daemon = True
|
||||
self.process_runner = process_runner
|
||||
self.provider = get_node_provider(provider_config, cluster_name)
|
||||
@@ -66,13 +72,12 @@ class NodeUpdater(object):
|
||||
"NodeUpdater: Error updating {}"
|
||||
"See {} for remote logs.".format(error_str, self.output_name),
|
||||
file=self.stdout)
|
||||
self.provider.set_node_tags(
|
||||
self.node_id, {TAG_RAY_NODE_STATUS: "UpdateFailed"})
|
||||
self.provider.set_node_tags(self.node_id,
|
||||
{TAG_RAY_NODE_STATUS: "UpdateFailed"})
|
||||
if self.logfile is not None:
|
||||
print(
|
||||
"----- BEGIN REMOTE LOGS -----\n" +
|
||||
open(self.logfile.name).read() +
|
||||
"\n----- END REMOTE LOGS -----")
|
||||
print("----- BEGIN REMOTE LOGS -----\n" + open(
|
||||
self.logfile.name).read() + "\n----- END REMOTE LOGS -----"
|
||||
)
|
||||
raise e
|
||||
self.provider.set_node_tags(
|
||||
self.node_id, {
|
||||
@@ -85,8 +90,8 @@ class NodeUpdater(object):
|
||||
file=self.stdout)
|
||||
|
||||
def do_update(self):
|
||||
self.provider.set_node_tags(
|
||||
self.node_id, {TAG_RAY_NODE_STATUS: "WaitingForSSH"})
|
||||
self.provider.set_node_tags(self.node_id,
|
||||
{TAG_RAY_NODE_STATUS: "WaitingForSSH"})
|
||||
deadline = time.time() + NODE_START_WAIT_S
|
||||
|
||||
# Wait for external IP
|
||||
@@ -114,7 +119,8 @@ class NodeUpdater(object):
|
||||
raise Exception("Node not running yet...")
|
||||
self.ssh_cmd(
|
||||
"uptime",
|
||||
connect_timeout=5, redirect=open("/dev/null", "w"))
|
||||
connect_timeout=5,
|
||||
redirect=open("/dev/null", "w"))
|
||||
ssh_ok = True
|
||||
except Exception as e:
|
||||
retry_str = str(e)
|
||||
@@ -130,8 +136,8 @@ class NodeUpdater(object):
|
||||
assert ssh_ok, "Unable to SSH to node"
|
||||
|
||||
# Rsync file mounts
|
||||
self.provider.set_node_tags(
|
||||
self.node_id, {TAG_RAY_NODE_STATUS: "SyncingFiles"})
|
||||
self.provider.set_node_tags(self.node_id,
|
||||
{TAG_RAY_NODE_STATUS: "SyncingFiles"})
|
||||
for remote_path, local_path in self.file_mounts.items():
|
||||
print(
|
||||
"NodeUpdater: Syncing {} to {}...".format(
|
||||
@@ -143,18 +149,20 @@ class NodeUpdater(object):
|
||||
local_path += "/"
|
||||
if not remote_path.endswith("/"):
|
||||
remote_path += "/"
|
||||
self.ssh_cmd(
|
||||
"mkdir -p {}".format(os.path.dirname(remote_path)))
|
||||
self.process_runner.check_call([
|
||||
"rsync", "-e", "ssh -i {} ".format(self.ssh_private_key) +
|
||||
"-o ConnectTimeout=120s -o StrictHostKeyChecking=no",
|
||||
"--delete", "-avz", "{}".format(local_path),
|
||||
"{}@{}:{}".format(self.ssh_user, self.ssh_ip, remote_path)
|
||||
], stdout=self.stdout, stderr=self.stderr)
|
||||
self.ssh_cmd("mkdir -p {}".format(os.path.dirname(remote_path)))
|
||||
self.process_runner.check_call(
|
||||
[
|
||||
"rsync", "-e", "ssh -i {} ".format(self.ssh_private_key) +
|
||||
"-o ConnectTimeout=120s -o StrictHostKeyChecking=no",
|
||||
"--delete", "-avz", "{}".format(local_path),
|
||||
"{}@{}:{}".format(self.ssh_user, self.ssh_ip, remote_path)
|
||||
],
|
||||
stdout=self.stdout,
|
||||
stderr=self.stderr)
|
||||
|
||||
# Run init commands
|
||||
self.provider.set_node_tags(
|
||||
self.node_id, {TAG_RAY_NODE_STATUS: "SettingUp"})
|
||||
self.provider.set_node_tags(self.node_id,
|
||||
{TAG_RAY_NODE_STATUS: "SettingUp"})
|
||||
for cmd in self.setup_cmds:
|
||||
self.ssh_cmd(cmd, verbose=True)
|
||||
|
||||
@@ -165,13 +173,16 @@ class NodeUpdater(object):
|
||||
pretty_cmd(cmd), self.ssh_ip),
|
||||
file=self.stdout)
|
||||
force_interactive = "set -i && source ~/.bashrc && "
|
||||
self.process_runner.check_call([
|
||||
"ssh", "-o", "ConnectTimeout={}s".format(connect_timeout),
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-i", self.ssh_private_key,
|
||||
"{}@{}".format(self.ssh_user, self.ssh_ip),
|
||||
"bash --login -c {}".format(pipes.quote(force_interactive + cmd))
|
||||
], stdout=redirect or self.stdout, stderr=redirect or self.stderr)
|
||||
self.process_runner.check_call(
|
||||
[
|
||||
"ssh", "-o", "ConnectTimeout={}s".format(connect_timeout),
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-i", self.ssh_private_key, "{}@{}".format(
|
||||
self.ssh_user, self.ssh_ip), "bash --login -c {}".format(
|
||||
pipes.quote(force_interactive + cmd))
|
||||
],
|
||||
stdout=redirect or self.stdout,
|
||||
stderr=redirect or self.stderr)
|
||||
|
||||
|
||||
class NodeUpdaterProcess(NodeUpdater, Process):
|
||||
|
||||
Reference in New Issue
Block a user