openclaw / Dockerfile
znfwz's picture
Update Dockerfile
91b07df verified
# 核心镜像选择
FROM node:22-slim
# 1. 基础依赖补全
RUN apt-get update && apt-get install -y --no-install-recommends \
git openssh-client build-essential python3 python3-pip \
g++ make ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 2. 安装 HF 数据交互工具
RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages
# 3. 构建环境与 Git 协议优化
RUN update-ca-certificates && \
git config --global http.sslVerify false && \
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
# 4. OpenClaw 核心安装
RUN npm install -g openclaw@latest --unsafe-perm
# 5. 安装飞书频道插件(预装)
RUN npm install -g @openclaw/feishu --unsafe-perm
# 6. 环境变量预设
ENV PORT=7860 \
OPENCLAW_GATEWAY_MODE=local \
HOME=/root
# 7. Python 同步引擎 (sync.py) - 增强版(json配置+压缩数据,直接覆盖配置)
RUN echo 'import os, sys, tarfile\n\
from huggingface_hub import HfApi, hf_hub_download\n\
from datetime import datetime, timedelta\n\
import json\n\
import shutil\n\
\n\
api = HfApi()\n\
repo_id = os.getenv("HF_DATASET")\n\
token = os.getenv("HF_TOKEN")\n\
OPENCLAW_DIR = "/root/.openclaw"\n\
\n\
BACKUP_FILES = [\n\
"agents",\n\
"credentials",\n\
"openclaw.json",\n\
"workspace/IDENTITY.md",\n\
"workspace/USER.md",\n\
"workspace/SOUL.md",\n\
"workspace/AGENTS.md",\n\
"workspace/TOOLS.md",\n\
"workspace/MEMORY.md",\n\
"workspace/skills",\n\
]\n\
\n\
def restore():\n\
try:\n\
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\
now = datetime.now()\n\
restored = False\n\
for i in range(5):\n\
day = (now - timedelta(days=i)).strftime("%Y-%m-%d")\n\
config_name = f"backup_{day}_config.json"\n\
data_name = f"backup_{day}_data.tar.gz"\n\
\n\
# 1. 恢复数据包(如果存在)\n\
if data_name in files:\n\
data_path = hf_hub_download(repo_id=repo_id, filename=data_name, repo_type="dataset", token=token)\n\
with tarfile.open(data_path, "r:gz") as tar:\n\
tar.extractall(path=OPENCLAW_DIR)\n\
print(f"Restored data from {data_name}")\n\
restored = True\n\
\n\
# 2. 恢复配置文件(如果存在),直接覆盖现有配置\n\
if config_name in files:\n\
config_path = hf_hub_download(repo_id=repo_id, filename=config_name, repo_type="dataset", token=token)\n\
target_config_path = os.path.join(OPENCLAW_DIR, "openclaw.json")\n\
# 确保目标目录存在\n\
os.makedirs(os.path.dirname(target_config_path), exist_ok=True)\n\
shutil.copy2(config_path, target_config_path)\n\
print(f"Restored config from {config_name} (overwritten)")\n\
restored = True\n\
\n\
if restored:\n\
print("Restore completed.")\n\
return True\n\
\n\
print("No backup found in last 5 days")\n\
return False\n\
except Exception as e:\n\
print(f"Restore Error: {e}")\n\
return False\n\
\n\
def backup():\n\
try:\n\
day = datetime.now().strftime("%Y-%m-%d")\n\
config_name = f"backup_{day}_config.json"\n\
data_name = f"backup_{day}_data.tar.gz"\n\
\n\
# 1. 备份配置文件(直接上传 JSON,不压缩)\n\
config_path = os.path.join(OPENCLAW_DIR, "openclaw.json")\n\
if os.path.exists(config_path):\n\
with open(config_path, "rb") as f:\n\
api.upload_file(\n\
path_or_fileobj=f,\n\
path_in_repo=config_name,\n\
repo_id=repo_id,\n\
repo_type="dataset",\n\
token=token\n\
)\n\
print(f"Config backup {config_name} uploaded.")\n\
\n\
# 2. 备份其他文件(打包为 tar.gz)\n\
with tarfile.open(data_name, "w:gz") as tar:\n\
for f in BACKUP_FILES:\n\
if f == "openclaw.json":\n\
continue # 已单独处理\n\
path = os.path.join(OPENCLAW_DIR, f)\n\
if os.path.exists(path):\n\
tar.add(path, arcname=f)\n\
print(f"Backed up: {f}")\n\
if os.path.exists(data_name):\n\
api.upload_file(\n\
path_or_fileobj=data_name,\n\
path_in_repo=data_name,\n\
repo_id=repo_id,\n\
repo_type="dataset",\n\
token=token\n\
)\n\
print(f"Data backup {data_name} uploaded.")\n\
\n\
print(f"Backup {day} completed.")\n\
except Exception as e:\n\
print(f"Backup Error: {e}")\n\
\n\
if __name__ == "__main__":\n\
if len(sys.argv) > 1 and sys.argv[1] == "backup": backup()\n\
else: restore()' > /usr/local/bin/sync.py
#RK|# 8. 启动控制逻辑
RUN cat > /usr/local/bin/start-openclaw << 'SCRIPT'
#!/bin/bash
set -e
mkdir -p /root/.openclaw/sessions
# 阶段 1: 清理可能存在的重复插件(使用全局 npm 安装的版本)
rm -rf /root/.openclaw/extensions/feishu
# 阶段 2: 执行启动前恢复(会清理旧的 feishu 配置)
python3 /usr/local/bin/sync.py restore
# 处理地址逻辑
CLEAN_BASE=$(echo "$OPENAI_API_BASE" | sed "s|/chat/completions||g" | sed "s|/v1/|/v1|g" | sed "s|/v1$|/v1|g")
# 阶段 3: 仅当配置文件不存在时才生成默认配置
if [ ! -f /root/.openclaw/openclaw.json ]; then
echo "No existing openclaw.json found, generating default configuration..."
cat > /root/.openclaw/openclaw.json <<EOF
{
"models": {
"providers": {
"$MODEL_PROVIDER": {
"baseUrl": "$OPENAI_API_BASE",
"apiKey": "$OPENAI_API_KEY",
"api": "openai-completions",
"models": [{
"id": "$MODEL",
"name": "$MODEL_NAME",
"contextWindow": $MODEL_CONTEXT
}]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "$MODEL_PROVIDER/$MODEL"
}
}
},
"gateway": {
"mode": "local",
"bind": "lan",
"port": $PORT,
"trustedProxies": ["0.0.0.0/0", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
"auth": {
"mode": "token",
"token": "$OPENCLAW_GATEWAY_TOKEN"
},
"remote": {
"token": "$OPENCLAW_GATEWAY_TOKEN"
},
"controlUi": {
"allowInsecureAuth": true,
"dangerouslyAllowHostHeaderOriginFallback": true,
"dangerouslyDisableDeviceAuth": true
}
}
}
EOF
else
echo "Existing openclaw.json found, skipping generation."
fi
# 阶段 4: doctor 自动检测并配置 feishu 插件
openclaw doctor --fix
# 阶段 5: 配置 feishu 频道(doctor 之后)
if [ -n "$FEISHU_APP_ID" ] && [ -n "$FEISHU_APP_SECRET" ]; then
openclaw config set channels.feishu.enabled true
openclaw config set channels.feishu.accounts.main.appId "$FEISHU_APP_ID"
openclaw config set channels.feishu.accounts.main.appSecret "$FEISHU_APP_SECRET"
fi
# 增量备份循环 (每 3小时)
(while true; do sleep 10800; python3 /usr/local/bin/sync.py backup; done) &
exec openclaw gateway run --port $PORT
SCRIPT
RUN chmod +x /usr/local/bin/start-openclaw
EXPOSE 7860
CMD ["/usr/local/bin/start-openclaw"]