itstheraj commited on
Commit
1f7e277
·
verified ·
1 Parent(s): 77d70dd

fleet: spot/restore/checkpoint + systemd self-heal (launch_ec2.py)

Browse files
Files changed (1) hide show
  1. benchmark/tabarena/launch_ec2.py +191 -0
benchmark/tabarena/launch_ec2.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Launch the FelaTab x TabArena benchmark on EC2 (replaces the unreliable HF Jobs path).
2
+
3
+ Pushes the current benchmark scripts to the HF model repo, then boots an on-demand
4
+ c7i.8xlarge (32 vCPU / 64 GiB, ~$1.43/hr in us-west-2) whose user-data runs
5
+ ec2_run.sh: lite shakedown -> (guard) -> full suite -> upload artifacts to
6
+ lowdown-labs/fela-tab-tabarena-results -> poweroff (shutdown behavior = terminate).
7
+
8
+ Prereqs: `aws login` (SSO session), HF_TOKEN in env (write token for lowdown-labs).
9
+
10
+ Usage:
11
+ HF_TOKEN=hf_... python launch_ec2.py # lite shakedown then full
12
+ HF_TOKEN=hf_... python launch_ec2.py --phases lite # shakedown only
13
+ HF_TOKEN=hf_... python launch_ec2.py --phases full --datasets "houses,diamonds,..." # shard
14
+
15
+ Note: HF_TOKEN is embedded in EC2 user-data (visible to anyone with
16
+ DescribeInstanceAttribute permission on this account). Rotate the token after the run
17
+ if that matters to you.
18
+ """
19
+
20
+ import argparse
21
+ import os
22
+ import time
23
+ import urllib.request
24
+
25
+ import boto3
26
+ from huggingface_hub import HfApi, get_token
27
+
28
+ MODEL_REPO = "lowdown-labs/fela-tab"
29
+ INSTANCE_TYPE = "c7i.8xlarge"
30
+ KEY_NAME = "felatab-tabarena"
31
+ SG_NAME = "felatab-tabarena-ssh"
32
+ AMI_PARAM = "/aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id"
33
+
34
+ USER_DATA_TMPL = """#!/bin/bash
35
+ set -euxo pipefail
36
+ export HOME=/root
37
+ export DEBIAN_FRONTEND=noninteractive
38
+ apt-get update -qq
39
+ apt-get install -y -qq git curl python3 python3-venv
40
+ curl -LsSf https://astral.sh/uv/install.sh | sh
41
+ export PATH="$HOME/.local/bin:$PATH"
42
+ uv venv --seed /work-bootvenv
43
+ uv pip install --python /work-bootvenv/bin/python -q huggingface_hub
44
+ mkdir -p /work
45
+ export HF_TOKEN="{hf_token}"
46
+ /work-bootvenv/bin/python -c "from huggingface_hub import snapshot_download; snapshot_download('{model_repo}', allow_patterns=['benchmark/tabarena/ec2_run.sh'], local_dir='/work/fela-tab')"
47
+ cat > /etc/felatab.env <<EOF
48
+ HF_TOKEN={hf_token}
49
+ PHASES={phases}
50
+ RUN_NAME={run_name}
51
+ DATASETS={datasets}
52
+ WITH_LGBM={with_lgbm}
53
+ SELF_TERMINATE=1
54
+ CHECKPOINT={checkpoint}
55
+ RESTORE={restore}
56
+ EOF
57
+ chmod 600 /etc/felatab.env
58
+ cat > /etc/systemd/system/felatab-benchmark.service <<'EOF'
59
+ [Unit]
60
+ Description=FelaTab TabArena benchmark
61
+ After=network-online.target
62
+ Wants=network-online.target
63
+
64
+ [Service]
65
+ Type=simple
66
+ EnvironmentFile=/etc/felatab.env
67
+ ExecStart=/bin/bash /work/fela-tab/benchmark/tabarena/ec2_run.sh
68
+ User=root
69
+ StandardOutput=append:/work/logs_boot.log
70
+ StandardError=append:/work/logs_boot.log
71
+
72
+ [Install]
73
+ WantedBy=multi-user.target
74
+ EOF
75
+ systemctl daemon-reload
76
+ systemctl enable --now felatab-benchmark.service
77
+ """
78
+
79
+
80
+ def ensure_key_pair(ec2, key_path):
81
+ names = {k["KeyName"] for k in ec2.describe_key_pairs()["KeyPairs"]}
82
+ if KEY_NAME in names:
83
+ print(f"key pair {KEY_NAME} exists")
84
+ return
85
+ kp = ec2.create_key_pair(KeyName=KEY_NAME)
86
+ with open(key_path, "w") as f:
87
+ f.write(kp["KeyMaterial"])
88
+ os.chmod(key_path, 0o400)
89
+ print(f"created key pair {KEY_NAME} -> {key_path}")
90
+
91
+
92
+ def ensure_security_group(ec2):
93
+ vpcs = ec2.describe_vpcs(Filters=[{"Name": "isDefault", "Values": ["true"]}])["Vpcs"]
94
+ vpc_id = vpcs[0]["VpcId"]
95
+ for sg in ec2.describe_security_groups(
96
+ Filters=[{"Name": "group-name", "Values": [SG_NAME]}, {"Name": "vpc-id", "Values": [vpc_id]}]
97
+ )["SecurityGroups"]:
98
+ print(f"security group {SG_NAME} exists ({sg['GroupId']})")
99
+ return sg["GroupId"]
100
+ my_ip = urllib.request.urlopen("https://checkip.amazonaws.com").read().decode().strip()
101
+ sg = ec2.create_security_group(GroupName=SG_NAME, Description="FelaTab TabArena runner (ssh)", VpcId=vpc_id)
102
+ ec2.authorize_security_group_ingress(
103
+ GroupId=sg["GroupId"],
104
+ IpPermissions=[{"IpProtocol": "tcp", "FromPort": 22, "ToPort": 22,
105
+ "IpRanges": [{"CidrIp": f"{my_ip}/32", "Description": "launcher ip"}]}],
106
+ )
107
+ print(f"created security group {SG_NAME} ({sg['GroupId']}), ssh from {my_ip}/32")
108
+ return sg["GroupId"]
109
+
110
+
111
+ def main():
112
+ ap = argparse.ArgumentParser()
113
+ ap.add_argument("--phases", default="lite,full", help="comma list: lite,full or just lite / full")
114
+ ap.add_argument("--datasets", default="", help="optional csv shard (full mode only)")
115
+ ap.add_argument("--run-name", default="felatab_ec2")
116
+ ap.add_argument("--instance-type", default=INSTANCE_TYPE)
117
+ ap.add_argument("--with-lgbm", default="0")
118
+ ap.add_argument("--spot", action="store_true",
119
+ help="persistent spot with stop-on-interruption: EBS cache survives, "
120
+ "systemd service resumes the run on restart")
121
+ ap.add_argument("--checkpoint", default="0", choices=["0", "1"],
122
+ help="upload results cache to the HF repo every 30 min")
123
+ ap.add_argument("--restore", default="0", choices=["0", "1"],
124
+ help="download existing raw/<run-name> cache before running")
125
+ args = ap.parse_args()
126
+
127
+ token = os.environ.get("HF_TOKEN") or get_token()
128
+ assert token, "need HF_TOKEN in env or a logged-in `hf auth login` session"
129
+ api = HfApi(token=token)
130
+
131
+ here = os.path.dirname(os.path.abspath(__file__))
132
+ for fname in ("ec2_run.sh", "run_tabarena.py", "fela_ag_model.py"):
133
+ api.upload_file(
134
+ path_or_fileobj=os.path.join(here, fname),
135
+ path_in_repo=f"benchmark/tabarena/{fname}",
136
+ repo_id=MODEL_REPO, repo_type="model",
137
+ commit_message=f"Update TabArena EC2 runner scripts ({fname})",
138
+ )
139
+ print(f"uploaded {fname} -> {MODEL_REPO}")
140
+
141
+ ec2 = boto3.client("ec2")
142
+ ssm = boto3.client("ssm")
143
+ ami = ssm.get_parameter(Name=AMI_PARAM)["Parameter"]["Value"]
144
+ print(f"AMI: {ami}")
145
+
146
+ key_path = os.path.expanduser(f"~/.aws/{KEY_NAME}.pem")
147
+ ensure_key_pair(ec2, key_path)
148
+ sg_id = ensure_security_group(ec2)
149
+
150
+ user_data = USER_DATA_TMPL.format(
151
+ hf_token=token, model_repo=MODEL_REPO, phases=args.phases,
152
+ run_name=args.run_name, datasets=args.datasets, with_lgbm=args.with_lgbm,
153
+ checkpoint=args.checkpoint, restore=args.restore,
154
+ )
155
+ kwargs = dict(
156
+ ImageId=ami,
157
+ InstanceType=args.instance_type,
158
+ KeyName=KEY_NAME,
159
+ MinCount=1, MaxCount=1,
160
+ NetworkInterfaces=[{"DeviceIndex": 0, "Groups": [sg_id], "AssociatePublicIpAddress": True}],
161
+ BlockDeviceMappings=[{"DeviceName": "/dev/sda1",
162
+ "Ebs": {"VolumeSize": 200, "VolumeType": "gp3", "DeleteOnTermination": True}}],
163
+ TagSpecifications=[{"ResourceType": "instance",
164
+ "Tags": [{"Key": "Name", "Value": f"felatab-tabarena-{args.run_name}"}]}],
165
+ UserData=user_data,
166
+ )
167
+ if args.spot:
168
+ # persistent spot, stop (not terminate) on interruption: the EBS results cache
169
+ # survives and the systemd service resumes the run when capacity returns
170
+ kwargs["InstanceMarketOptions"] = {
171
+ "MarketType": "spot",
172
+ "SpotOptions": {"SpotInstanceType": "persistent", "InstanceInterruptionBehavior": "stop"},
173
+ }
174
+ kwargs["InstanceInitiatedShutdownBehavior"] = "stop"
175
+ else:
176
+ kwargs["InstanceInitiatedShutdownBehavior"] = "terminate"
177
+ resp = ec2.run_instances(**kwargs)
178
+ iid = resp["Instances"][0]["InstanceId"]
179
+ print(f"instance: {iid} ({args.instance_type}, phases={args.phases}, run={args.run_name})")
180
+
181
+ ec2.get_waiter("instance_running").wait(InstanceIds=[iid])
182
+ desc = ec2.describe_instances(InstanceIds=[iid])["Reservations"][0]["Instances"][0]
183
+ ip = desc.get("PublicIpAddress", "<no public ip>")
184
+ print(f"running at {ip}")
185
+ print(f"watch: ssh -i {key_path} ubuntu@{ip} 'tail -f /work/logs_boot.log'")
186
+ print(f"status: aws ec2 describe-instances --instance-ids {iid} --query 'Reservations[0].Instances[0].State.Name'")
187
+ print("artifacts land in https://huggingface.co/datasets/lowdown-labs/fela-tab-tabarena-results")
188
+
189
+
190
+ if __name__ == "__main__":
191
+ main()