Skip to content

Commit 5c4fca2

Browse files
committed
Fix userdata size limit by using compact template
The userdata script exceeded AWS's 16KB limit. Created a highly optimized compact template that: - Reduces from 18KB to 4.7KB - Uses minimal variable names (H for homedir, RT for runner token, etc.) - Removes verbose logging and comments - Consolidates multi-line scripts into single lines - Abbreviates file paths (/var/run/github-runner-* -> /var/run/ghr-*) - Simplifies job tracking to minimal JSON The compact template maintains all essential functionality: - Home directory auto-detection - Failsafe termination on errors - Registration timeout watchdog - CloudWatch logging - SSH key setup - Job hooks for termination logic
1 parent 1a7deb2 commit 5c4fca2

3 files changed

Lines changed: 112 additions & 8 deletions

File tree

src/ec2_gha/start.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ def _build_user_data(self, **kwargs) -> str:
208208
The user data script as a string.
209209
210210
"""
211-
template = importlib.resources.files("ec2_gha").joinpath("templates/user-script.sh.templ")
211+
# Use compact template to stay under 16KB userdata limit
212+
template = importlib.resources.files("ec2_gha").joinpath("templates/user-script-compact.sh.templ")
212213
with template.open() as f:
213214
template_content = f.read()
214215

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/bin/bash
2+
set -e
3+
INSTANCE_ID=$$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
4+
terminate_instance() {
5+
echo "FATAL: $$1" | tee -a /var/log/runner-setup.log
6+
[ -f "$$H/config.sh" ] && cd "$$H" && ./config.sh remove --token "$${RT}" 2>/dev/null || true
7+
shutdown -h now
8+
}
9+
trap 'terminate_instance "Error line $$LINENO"' ERR
10+
(sleep $${runner_registration_timeout:-300}; [ ! -f /var/run/ghr-reg ] && terminate_instance "Registration timeout") &
11+
RWP=$$!
12+
$userdata
13+
H="$homedir"
14+
[ -z "$$H" -o "$$H" = "AUTO" ] && for u in ubuntu ec2-user centos admin debian fedora; do
15+
id $$u &>/dev/null && H="/home/$$u" && break
16+
done
17+
[ -z "$$H" -o "$$H" = "AUTO" ] && H=$$(getent passwd|awk -F: '$$3>=1000&&$$3<65534&&$$6~/^\/home\//{print $$6;exit}')
18+
[ -z "$$H" ] && H="/home/ec2-user"
19+
cd "$$H"
20+
nohup bash -c "sleep $${max_instance_lifetime}m && shutdown -h now" &>/dev/null &
21+
if [ "$cloudwatch_logs_group" != "" ]; then
22+
apt-get update && apt-get install -y amazon-cloudwatch-agent || yum install -y amazon-cloudwatch-agent
23+
cat>/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json<<EOF
24+
{"logs":{"logs_collected":{"files":{"collect_list":[
25+
{"file_path":"/var/log/runner-setup.log","log_group_name":"$cloudwatch_logs_group","log_stream_name":"$${INSTANCE_ID}/runner-setup"},
26+
{"file_path":"/tmp/job-started-hook.log","log_group_name":"$cloudwatch_logs_group","log_stream_name":"$${INSTANCE_ID}/job-started"},
27+
{"file_path":"/tmp/job-completed-hook.log","log_group_name":"$cloudwatch_logs_group","log_stream_name":"$${INSTANCE_ID}/job-completed"},
28+
{"file_path":"/tmp/termination-check.log","log_group_name":"$cloudwatch_logs_group","log_stream_name":"$${INSTANCE_ID}/termination"},
29+
{"file_path":"$$H/_diag/Runner_**.log","log_group_name":"$cloudwatch_logs_group","log_stream_name":"$${INSTANCE_ID}/runner-diag"},
30+
{"file_path":"$$H/_diag/Worker_**.log","log_group_name":"$cloudwatch_logs_group","log_stream_name":"$${INSTANCE_ID}/worker-diag"}
31+
]}}}}
32+
EOF
33+
systemctl start amazon-cloudwatch-agent
34+
fi
35+
[ "$ssh_pubkey" != "" ] && mkdir -p $$H/.ssh && echo "$ssh_pubkey">>$$H/.ssh/authorized_keys && chmod 700 $$H/.ssh && chmod 600 $$H/.ssh/authorized_keys
36+
exec &>/var/log/runner-setup.log
37+
echo "$script" > pre-runner-script.sh && source pre-runner-script.sh
38+
export RUNNER_ALLOW_RUNASROOT=1
39+
curl -L $runner_release -o runner.tar.gz && tar --no-overwrite-dir -xzf runner.tar.gz
40+
cat>/usr/local/bin/job-started-hook.sh<<'E'
41+
#!/bin/bash
42+
exec>>/tmp/job-started-hook.log 2>&1
43+
echo "[$$(date)] Job started: $$GITHUB_JOB"
44+
touch /var/run/ghr-act
45+
mkdir -p /var/run/ghr-jobs
46+
echo '{"s":"r"}' > /var/run/ghr-jobs/$$GITHUB_RUN_ID-$$GITHUB_JOB.job
47+
E
48+
cat>/usr/local/bin/job-completed-hook.sh<<'E'
49+
#!/bin/bash
50+
exec>>/tmp/job-completed-hook.log 2>&1
51+
echo "[$$(date)] Job completed: $$GITHUB_JOB"
52+
rm -f /var/run/ghr-jobs/$$GITHUB_RUN_ID-$$GITHUB_JOB.job
53+
touch /var/run/ghr-act
54+
E
55+
cat>/usr/local/bin/check-runner-termination.sh<<'E'
56+
#!/bin/bash
57+
exec>>/tmp/termination-check.log 2>&1
58+
[ ! -f /var/run/ghr-act ] && exit 0
59+
L=$$(stat -c %Y /var/run/ghr-act 2>/dev/null || stat -f %m /var/run/ghr-act)
60+
I=$$(($$(date +%s)-L))
61+
G=$${RUNNER_GRACE_PERIOD:-60}
62+
[ ! -f /var/run/ghr-started ] && G=$${RUNNER_INITIAL_GRACE_PERIOD:-180}
63+
R=$$(ls /var/run/ghr-jobs/*.job 2>/dev/null|wc -l)
64+
[ $$R -eq 0 -a $$I -gt $$G ] && { [ -f $$H/config.sh ] && cd $$H && RUNNER_ALLOW_RUNASROOT=1 ./config.sh remove --token $${RT} || true; shutdown -h now; }
65+
E
66+
chmod +x /usr/local/bin/*-hook.sh
67+
cat>/etc/systemd/system/ghr-term.service<<EOF
68+
[Unit]
69+
Description=Check Runner Term
70+
[Service]
71+
Type=oneshot
72+
Environment="RUNNER_GRACE_PERIOD=$runner_grace_period"
73+
Environment="RUNNER_INITIAL_GRACE_PERIOD=$runner_initial_grace_period"
74+
Environment="RT=$token"
75+
Environment="H=$$H"
76+
ExecStart=/usr/local/bin/check-runner-termination.sh
77+
EOF
78+
cat>/etc/systemd/system/ghr-term.timer<<EOF
79+
[Unit]
80+
Description=Runner Term Timer
81+
[Timer]
82+
OnBootSec=60s
83+
OnUnitActiveSec=$${runner_poll_interval}s
84+
[Install]
85+
WantedBy=timers.target
86+
EOF
87+
systemctl daemon-reload && systemctl enable --now ghr-term.timer
88+
IT=$$(curl -s http://169.254.169.254/latest/meta-data/instance-type)
89+
RN="i-$${INSTANCE_ID:2}"
90+
ML=",instance-id:$${INSTANCE_ID},instance-type:$${IT},workflow:$github_workflow,run-id:$github_run_id,run-number:$github_run_number"
91+
AL="$labels$${ML}"
92+
export RT="$token"
93+
./config.sh --url https://github.com/$repo --token $token --labels "$$AL" --name "$$RN" --disableupdate
94+
touch /var/run/ghr-reg
95+
[ -n "$$RWP" ] && kill $$RWP 2>/dev/null || true
96+
touch /var/run/ghr-started
97+
mkdir -p $$H/_diag && chmod 755 $$H/_diag && chmod o+x $$H
98+
cat>.env<<EOF
99+
RUNNER_HOME=$$H
100+
ACTIONS_RUNNER_HOOK_JOB_STARTED=/usr/local/bin/job-started-hook.sh
101+
ACTIONS_RUNNER_HOOK_JOB_COMPLETED=/usr/local/bin/job-completed-hook.sh
102+
EOF
103+
./run.sh

src/ec2_gha/templates/user-script.sh.templ

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ REGISTRATION_TIMEOUT=$${runner_registration_timeout:-300}
3131
fi
3232
) &
3333
REGISTRATION_WATCHDOG_PID=$$!
34-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] Started registration watchdog (PID: $$REGISTRATION_WATCHDOG_PID, timeout: $${REGISTRATION_TIMEOUT}s)"
34+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Watchdog PID: $$REGISTRATION_WATCHDOG_PID"
3535

3636
$userdata
3737

@@ -45,7 +45,7 @@ if [ -z "$$homedir" ] || [ "$$homedir" = "AUTO" ]; then
4545
for user in ubuntu ec2-user centos admin debian fedora alpine arch; do
4646
if id "$$user" &>/dev/null; then
4747
homedir="/home/$$user"
48-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] Auto-detected home directory for user $$user: $$homedir"
48+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Auto-detected: $$homedir"
4949
break
5050
fi
5151
done
@@ -62,20 +62,20 @@ if [ -z "$$homedir" ] || [ "$$homedir" = "AUTO" ]; then
6262

6363
if [ -z "$$homedir" ]; then
6464
homedir="/home/ec2-user" # Ultimate fallback
65-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] No suitable user found, using fallback: $$homedir"
65+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Using fallback: $$homedir"
6666
else
6767
# Use stat to get the actual owner of the directory
6868
owner=$$(stat -c "%U" "$$homedir" 2>/dev/null || stat -f "%Su" "$$homedir" 2>/dev/null)
69-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] Detected home directory for user $$owner: $$homedir"
69+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Detected: $$homedir ($$owner)"
7070
fi
7171
fi
7272
else
73-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] Using specified home directory: $$homedir"
73+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Using: $$homedir"
7474
fi
7575

7676
# Set up maximum lifetime timeout - do this early to ensure cleanup
7777
MAX_LIFETIME_MINUTES=$max_instance_lifetime
78-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] Setting up maximum lifetime timeout: $${MAX_LIFETIME_MINUTES} minutes"
78+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Max lifetime: $${MAX_LIFETIME_MINUTES}m"
7979
nohup bash -c "sleep $${MAX_LIFETIME_MINUTES}m && echo '[$$(date)] Maximum lifetime reached' && shutdown -h now" > /var/log/max-lifetime.log 2>&1 &
8080

8181
# Configure CloudWatch Logs if enabled
@@ -191,7 +191,7 @@ fi
191191

192192
# Redirect runner setup logs to a file for CloudWatch
193193
exec >> /var/log/runner-setup.log 2>&1
194-
echo "[$$(date '+%Y-%m-%d %H:%M:%S')] GitHub runner setup starting"
194+
echo "[$$( date '+%Y-%m-%d %H:%M:%S')] Starting runner setup"
195195

196196
# Log instance metadata for later analysis
197197
INSTANCE_TYPE=$$(curl -s http://169.254.169.254/latest/meta-data/instance-type 2>/dev/null || echo "unknown")

0 commit comments

Comments
 (0)