kyle-ai commited on
Commit
7be6895
·
verified ·
1 Parent(s): 3de7e85

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +23 -64
Dockerfile CHANGED
@@ -1,28 +1,28 @@
1
  # 核心镜像
2
  FROM node:22-slim
3
 
4
- # 1. 安装系统依赖
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
6
  git build-essential python3 python3-pip \
7
- ca-certificates procps \
8
  && rm -rf /var/lib/apt/lists/*
9
 
10
  # 2. 安装 Python 依赖
11
  RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages
12
 
13
- # 3. 安装 OpenClaw (锁定最新稳定版本)
14
  ARG OPENCLAW_VERSION=2026.2.19
15
- ARG CACHEBUST=41
16
- RUN npm install -g openclaw@${OPENCLAW_VERSION} --registry=https://registry.npmjs.org/ --unsafe-perm=true --foreground-scripts && npm cache clean --force
17
 
18
- # 4. 设置环境变量
19
- ENV PORT=7860 \
 
20
  HOME=/root \
21
  OPENCLAW_TRUST_LOCAL_WS=1 \
22
  OPENCLAW_SECURITY_STRICT=false \
23
  NODE_TLS_REJECT_UNAUTHORIZED=0
24
 
25
- # 5. 同步引擎 (Python 备份/恢复逻辑)
26
  RUN echo 'import os, sys, tarfile, time\n\
27
  from huggingface_hub import HfApi, hf_hub_download\n\
28
  from datetime import datetime, timedelta\n\
@@ -32,11 +32,7 @@ token = os.getenv("HF_TOKEN")\n\
32
  base_dir = "/root"\n\
33
  \n\
34
  def restore():\n\
35
- now_ts = datetime.now().strftime("%H:%M:%S")\n\
36
- print(f"--- [Sync] 🚀 启动恢复流程 [{now_ts}] ---")\n\
37
- if not repo_id or not token: \n\
38
- print("--- [Sync] ⚠️ 跳过恢复: 未配置 HF_DATASET 或 HF_TOKEN ---")\n\
39
- return False\n\
40
  try:\n\
41
  files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\
42
  now = datetime.now()\n\
@@ -44,32 +40,21 @@ def restore():\n\
44
  day = (now - timedelta(days=i)).strftime("%Y-%m-%d")\n\
45
  name = f"backup_{day}.tar.gz"\n\
46
  if name in files:\n\
47
- print(f"--- [Sync] 📥 正在从 HF 下载备份: {name} ... ---")\n\
48
  path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token)\n\
49
  with tarfile.open(path, "r:gz") as tar: tar.extractall(path=base_dir)\n\
50
- print(f"--- [Sync] ✅ 恢复成功!(文件: {name}) ---")\n\
51
  return True\n\
52
- print("--- [Sync] ℹ️ 未发现最近 5 天的备份文件 ---")\n\
53
  except Exception as e: print(f"--- [Sync] ❌ 恢复失败: {e} ---")\n\
54
- return False\n\
55
  \n\
56
  def backup():\n\
57
  if not repo_id or not token: return\n\
58
- now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")\n\
59
- print(f"--- [Sync] 📤 开始定时备份任务 [{now_str}] ---")\n\
60
  try:\n\
61
  target_dir = "/root/.openclaw"\n\
62
- if not os.path.exists(target_dir):\n\
63
- print(f"--- [Sync] ⚠️ 跳过备份: 目录 {target_dir} 不存在 ---")\n\
64
- return\n\
65
  day = datetime.now().strftime("%Y-%m-%d")\n\
66
  name = f"backup_{day}.tar.gz"\n\
67
- with tarfile.open(name, "w:gz") as tar:\n\
68
- tar.add(target_dir, arcname=".openclaw")\n\
69
- file_size = os.path.getsize(name) / 1024\n\
70
- print(f"--- [Sync] 📦 压缩完成: {name} ({file_size:.2f} KB) ---")\n\
71
  api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)\n\
72
- print(f"--- [Sync] ✨ 备份已同步至 Hugging Face ---")\n\
73
  if os.path.exists(name): os.remove(name)\n\
74
  except Exception as e: print(f"--- [Sync] ❌ 备份失败: {e} ---")\n\
75
  \n\
@@ -77,61 +62,35 @@ if __name__ == "__main__":\n\
77
  if len(sys.argv) > 1 and sys.argv[1] == "backup": backup()\n\
78
  else: restore()' > /usr/local/bin/sync.py
79
 
80
- # 6. 核心启动脚本 (修复 PID 1 退出导致容器挂掉的问题)
81
  RUN echo "#!/bin/bash\n\
82
- set -e\n\
83
  mkdir -p /root/.openclaw\n\
84
  \n\
85
- echo \"--- [System] 🛠️ 正在初始化环境... ---\"\n\
86
- python3 /usr/local/bin/sync.py restore\n\
87
  \n\
88
  CLEAN_BASE=\$(echo \"\$OPENAI_API_BASE\" | sed \"s|/chat/completions||g\" | sed \"s|/v1/|/v1|g\")\n\
89
  \n\
90
- # 动态写入 OpenClaw 配置文件\n\
91
  cat > /root/.openclaw/openclaw.json <<EOF\n\
92
  {\n\
93
  \"models\": { \"providers\": { \"siliconflow\": { \"baseUrl\": \"\$CLEAN_BASE\", \"apiKey\": \"\$OPENAI_API_KEY\", \"api\": \"openai-completions\", \"models\": [{ \"id\": \"\$MODEL\", \"name\": \"DeepSeek\", \"contextWindow\": 128000 }] } } },\n\
94
  \"agents\": { \"defaults\": { \"model\": { \"primary\": \"siliconflow/\$MODEL\" } } },\n\
95
  \"gateway\": {\n\
96
- \"mode\": \"local\",\n\
97
- \"bind\": \"lan\",\n\
98
- \"port\": \$PORT,\n\
99
  \"auth\": { \"mode\": \"token\", \"token\": \"\$OPENCLAW_GATEWAY_PASSWORD\" },\n\
100
  \"controlUi\": { \"allowInsecureAuth\": true }\n\
101
  }\n\
102
  }\n\
103
  EOF\n\
104
  \n\
105
- # 启动后台备份守护进程 (每 30 分钟同步一次)\n\
106
- (while true; do \n\
107
- sleep 1800; \n\
108
- echo \"--- [Cron] ⏰ \$(date '+%Y-%m-%d %H:%M:%S') 触发自动备份流程... ---\"; \n\
109
- python3 /usr/local/bin/sync.py backup; \n\
110
- done) &\n\
111
- \n\
112
- echo \"--- [Security] 🛡️ 已禁用严格安全检查并信任本地 WebSocket ---\"\n\
113
- \n\
114
- # 【修复核心】移除 exec,使用死循环接管 OpenClaw 进程\n\
115
- # 这样 Bash 将作为 PID 1 长期存在,OpenClaw 内部重启(退出)后会被脚本重新拉起\n\
116
- while true; do\n\
117
- START_TIME=\$(date +%s)\n\
118
- echo \"--- [OpenClaw] 🚀 正在启动网关 (Port: \$PORT)... ---\"\n\
119
- \n\
120
- # 正常启动网关 (前台阻塞运行)\n\
121
- openclaw gateway run --port \$PORT\n\
122
- \n\
123
- END_TIME=\$(date +%s)\n\
124
- DURATION=\$((END_TIME - START_TIME))\n\
125
  \n\
126
- # 策略:如果进程存活时间太短(小于 5 秒),说明可能是配置错误,增加重试延迟以防疯狂刷屏\n\
127
- if [ \$DURATION -lt 5 ]; then\n\
128
- echo \"--- [System] ⚠️ 进程检测到异常快速退出,10 秒后尝试重启... ---\"\n\
129
- sleep 10\n\
130
- else\n\
131
- echo \"--- [System] ℹ️ 触发重启逻辑(进程已结束),3 秒后重新拉起... ---\"\n\
132
- sleep 3\n\
133
- fi\n\
134
- done\n\
135
  " > /usr/local/bin/start-openclaw && chmod +x /usr/local/bin/start-openclaw
136
 
137
  EXPOSE 7860
 
1
  # 核心镜像
2
  FROM node:22-slim
3
 
4
+ # 1. 安装系统依赖 (TZDATA 用于处理时区)
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
6
  git build-essential python3 python3-pip \
7
+ ca-certificates procps tzdata \
8
  && rm -rf /var/lib/apt/lists/*
9
 
10
  # 2. 安装 Python 依赖
11
  RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages
12
 
13
+ # 3. 安装 OpenClaw 和 PM2 (PM2 是解决重启问题的核心)
14
  ARG OPENCLAW_VERSION=2026.2.19
15
+ RUN npm install -g openclaw@${OPENCLAW_VERSION} pm2 --registry=https://registry.npmjs.org/ --unsafe-perm=true --foreground-scripts && npm cache clean --force
 
16
 
17
+ # 4. 设置环境变量 (加入 TZ 时区设置)
18
+ ENV TZ=Asia/Shanghai \
19
+ PORT=7860 \
20
  HOME=/root \
21
  OPENCLAW_TRUST_LOCAL_WS=1 \
22
  OPENCLAW_SECURITY_STRICT=false \
23
  NODE_TLS_REJECT_UNAUTHORIZED=0
24
 
25
+ # 5. 同步引擎 (保持你原有的备份/恢复逻辑)
26
  RUN echo 'import os, sys, tarfile, time\n\
27
  from huggingface_hub import HfApi, hf_hub_download\n\
28
  from datetime import datetime, timedelta\n\
 
32
  base_dir = "/root"\n\
33
  \n\
34
  def restore():\n\
35
+ if not repo_id or not token: return\n\
 
 
 
 
36
  try:\n\
37
  files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\
38
  now = datetime.now()\n\
 
40
  day = (now - timedelta(days=i)).strftime("%Y-%m-%d")\n\
41
  name = f"backup_{day}.tar.gz"\n\
42
  if name in files:\n\
 
43
  path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token)\n\
44
  with tarfile.open(path, "r:gz") as tar: tar.extractall(path=base_dir)\n\
45
+ print(f"--- [Sync] ✅ 恢复成功: {name} ---")\n\
46
  return True\n\
 
47
  except Exception as e: print(f"--- [Sync] ❌ 恢复失败: {e} ---")\n\
 
48
  \n\
49
  def backup():\n\
50
  if not repo_id or not token: return\n\
 
 
51
  try:\n\
52
  target_dir = "/root/.openclaw"\n\
53
+ if not os.path.exists(target_dir): return\n\
 
 
54
  day = datetime.now().strftime("%Y-%m-%d")\n\
55
  name = f"backup_{day}.tar.gz"\n\
56
+ with tarfile.open(name, "w:gz") as tar: tar.add(target_dir, arcname=".openclaw")\n\
 
 
 
57
  api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)\n\
 
58
  if os.path.exists(name): os.remove(name)\n\
59
  except Exception as e: print(f"--- [Sync] ❌ 备份失败: {e} ---")\n\
60
  \n\
 
62
  if len(sys.argv) > 1 and sys.argv[1] == "backup": backup()\n\
63
  else: restore()' > /usr/local/bin/sync.py
64
 
65
+ # 6. 启动脚本 ( PM2 接管进程控制)
66
  RUN echo "#!/bin/bash\n\
 
67
  mkdir -p /root/.openclaw\n\
68
  \n\
69
+ echo \"--- [System] 🛠️ 初始化时区: \$(date) ---\"\n\
70
+ python3 /usr/local/bin/sync.py restore || true\n\
71
  \n\
72
  CLEAN_BASE=\$(echo \"\$OPENAI_API_BASE\" | sed \"s|/chat/completions||g\" | sed \"s|/v1/|/v1|g\")\n\
73
  \n\
 
74
  cat > /root/.openclaw/openclaw.json <<EOF\n\
75
  {\n\
76
  \"models\": { \"providers\": { \"siliconflow\": { \"baseUrl\": \"\$CLEAN_BASE\", \"apiKey\": \"\$OPENAI_API_KEY\", \"api\": \"openai-completions\", \"models\": [{ \"id\": \"\$MODEL\", \"name\": \"DeepSeek\", \"contextWindow\": 128000 }] } } },\n\
77
  \"agents\": { \"defaults\": { \"model\": { \"primary\": \"siliconflow/\$MODEL\" } } },\n\
78
  \"gateway\": {\n\
79
+ \"mode\": \"local\", \"bind\": \"lan\", \"port\": \$PORT,\n\
 
 
80
  \"auth\": { \"mode\": \"token\", \"token\": \"\$OPENCLAW_GATEWAY_PASSWORD\" },\n\
81
  \"controlUi\": { \"allowInsecureAuth\": true }\n\
82
  }\n\
83
  }\n\
84
  EOF\n\
85
  \n\
86
+ # 启动定时备份 (放在后台)\n\
87
+ (while true; do sleep 1800; python3 /usr/local/bin/sync.py backup; done) &\n\
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  \n\
89
+ # 使用 pm2-runtime 启动 OpenClaw\n\
90
+ # --watch 可以监控文件变化(可选),但这里主要利用它的进程自愈能力\n\
91
+ # pm2-runtime 会作为 PID 1,当 openclaw 退出它会自动重启 openclaw 而不退出容器\n\
92
+ echo \"--- [System] 🚀 使用 PM2 启动网关... ---\"\n\
93
+ exec pm2-runtime start \"openclaw gateway run --port \$PORT\" --name \"openclaw-gateway\"\n\
 
 
 
 
94
  " > /usr/local/bin/start-openclaw && chmod +x /usr/local/bin/start-openclaw
95
 
96
  EXPOSE 7860