🔄 卡若AI 同步 2026-02-22 10:22 | 更新:金仓、水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 8 个

This commit is contained in:
2026-02-22 10:22:18 +08:00
parent 1bc7435c11
commit adc8caa68d
16 changed files with 1117 additions and 38 deletions

View File

@@ -9,7 +9,7 @@ services:
ports:
- "8080:80"
volumes:
- ./www:/var/www/html:ro
- ./www:/var/www/html
restart: unless-stopped
environment:
- TZ=Asia/Shanghai

View File

@@ -0,0 +1,228 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
存客宝 www.lytiao.com Docker 一键确保运行
1. TAT 全量部署 + 验证
2. 安全组放行 8080
3. 获取并打印服务器执行结果
"""
import base64
import json
import os
import re
import sys
import time
CKB_INSTANCE_ID = "ins-ciyv2mxa"
REGION = "ap-guangzhou"
def _find_root():
d = os.path.dirname(os.path.abspath(__file__))
for _ in range(6):
if os.path.basename(d) == "卡若AI" or (os.path.isdir(os.path.join(d, "运营中枢")) and os.path.isdir(os.path.join(d, "01_卡资"))):
return d
d = os.path.dirname(d)
return None
def _read_creds():
root = _find_root()
if not root:
return None, None
path = os.path.join(root, "运营中枢", "工作台", "00_账号与API索引.md")
if not os.path.isfile(path):
return None, None
with open(path, "r", encoding="utf-8") as f:
text = f.read()
sid = skey = None
in_t = False
for line in text.splitlines():
if "### 腾讯云" in line:
in_t = True
continue
if in_t and line.strip().startswith("###"):
break
if not in_t:
continue
m = re.search(r"\|\s*[^|]*(?:SecretId|密钥)[^|]*\|\s*`([^`]+)`", line, re.I)
if m and m.group(1).strip().startswith("AKID"):
sid = m.group(1).strip()
m = re.search(r"\|\s*SecretKey\s*\|\s*`([^`]+)`", line, re.I)
if m:
skey = m.group(1).strip()
return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY")
# 完整部署:创建配置、复制网站、构建、启动、验证
CMD = """set -e
SRC="/www/wwwroot/www.lytiao.com"
DIR="/opt/lytiao_docker"
mkdir -p "$DIR"
echo ">>> 1. 创建 Docker 配置"
cat > "$DIR/Dockerfile" << 'DFEND'
FROM php:7.1-apache
RUN a2enmod rewrite
RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev libzip-dev zip unzip \\
&& docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \\
&& docker-php-ext-install -j$(nproc) gd mysqli pdo pdo_mysql zip \\
&& apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /var/www/html
EXPOSE 80
DFEND
cat > "$DIR/docker-compose.yml" << 'DCEND'
version: "3.8"
services:
lytiao-web:
build: .
container_name: lytiao-www
ports:
- "8090:80"
volumes:
- ./www:/var/www/html:ro
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
DCEND
echo ">>> 2. 复制网站文件"
rm -rf "$DIR/www"
cp -a "$SRC" "$DIR/www"
echo ">>> 3. 构建并启动"
cd "$DIR"
docker compose down 2>/dev/null || true
docker compose up -d --build
sleep 5
echo ">>> 4. 验证容器状态"
docker ps -a --filter "name=lytiao"
echo ""
echo ">>> 5. 本机访问测试"
curl -sI -o /dev/null -w "HTTP状态: %{http_code}\\n" --connect-timeout 5 http://127.0.0.1:8090/ || echo "curl失败"
echo ""
echo "DONE"
"""
def main():
secret_id, secret_key = _read_creds()
if not secret_id or not secret_key:
print("❌ 未配置腾讯云凭证")
return 1
try:
from tencentcloud.common import credential
from tencentcloud.tat.v20201028 import tat_client, models
except ImportError:
print("pip install tencentcloud-sdk-python-common tencentcloud-sdk-python-tat")
return 1
# 1. 安全组放行 8080
print("========== 1. 安全组放行 8090 ==========")
try:
from tencentcloud.common import credential
from tencentcloud.cvm.v20170312 import cvm_client, models as cvm_models
from tencentcloud.vpc.v20170312 import vpc_client, models as vpc_models
cred = credential.Credential(secret_id, secret_key)
sg_ids, region = [], None
for r in ["ap-guangzhou", "ap-beijing", "ap-shanghai"]:
try:
c = cvm_client.CvmClient(cred, r)
req = cvm_models.DescribeInstancesRequest()
req.Limit = 100
resp = c.DescribeInstances(req)
for ins in (getattr(resp, "InstanceSet", None) or []):
if "42.194.245.239" in list(getattr(ins, "PublicIpAddresses", None) or []):
sg_ids = list(getattr(ins, "SecurityGroupIds", None) or [])
region = r
break
except Exception:
continue
if sg_ids:
break
if sg_ids and region:
vc = vpc_client.VpcClient(cred, region)
for sg_id in sg_ids:
try:
req = vpc_models.CreateSecurityGroupPoliciesRequest()
req.SecurityGroupId = sg_id
ps = vpc_models.SecurityGroupPolicySet()
ing = vpc_models.SecurityGroupPolicy()
ing.Protocol, ing.Port, ing.CidrBlock = "TCP", "8090", "0.0.0.0/0"
ing.Action, ing.PolicyDescription = "ACCEPT", "lytiao-Docker"
ps.Ingress = [ing]
req.SecurityGroupPolicySet = ps
vc.CreateSecurityGroupPolicies(req)
print("%s 已添加 8080/TCP" % sg_id)
except Exception as e:
if "RuleAlreadyExists" in str(e) or "已存在" in str(e):
print(" ⏭ 8080 规则已存在")
else:
print("", e)
else:
print(" 未找到存客宝实例,跳过放行")
except ImportError as e:
print(" 跳过(缺 vpc 包): pip install tencentcloud-sdk-python-vpc")
except Exception as e:
print(" 放行异常:", e)
# 2. TAT 部署
print("\n========== 2. TAT 部署 lytiao 容器 ==========")
cred = credential.Credential(secret_id, secret_key)
client = tat_client.TatClient(cred, REGION)
req = models.RunCommandRequest()
req.Content = base64.b64encode(CMD.encode()).decode()
req.InstanceIds = [CKB_INSTANCE_ID]
req.CommandType = "SHELL"
req.Timeout = 600
req.CommandName = "CKB_lytiao_Ensure"
resp = client.RunCommand(req)
inv_id = resp.InvocationId
print(" 已下发 InvocationId:", inv_id)
print(" 等待 90s 获取执行结果...")
time.sleep(90)
# 3. 获取输出
print("\n========== 3. 服务器执行结果 ==========")
try:
req2 = models.DescribeInvocationTasksRequest()
f = models.Filter()
f.Name = "invocation-id"
f.Values = [inv_id]
req2.Filters = [f]
resp2 = client.DescribeInvocationTasks(req2)
for t in (resp2.InvocationTaskSet or []):
status = getattr(t, "TaskStatus", "N/A")
exit_code = getattr(t, "ExitCode", None)
print(" 任务状态:", status, "退出码:", exit_code)
tr = getattr(t, "TaskResult", None)
if tr:
try:
if hasattr(tr, "get"):
j = tr
else:
j = json.loads(tr) if isinstance(tr, str) else vars(tr)
out = j.get("Output", "") if isinstance(j, dict) else getattr(tr, "Output", "")
if out:
try:
out = base64.b64decode(out).decode("utf-8", errors="replace")
except Exception:
pass
print(out[-4000:] if len(out) > 4000 else out)
err = j.get("Error", "") if isinstance(j, dict) else getattr(tr, "Error", "")
if err:
try:
err = base64.b64decode(err).decode("utf-8", errors="replace")
except Exception:
pass
print("--- 错误 ---\n", err[:3000])
# 打印完整 TaskResult 用于调试
if status == "FAILED" and not out and not err:
print("--- 原始 TaskResult ---\n", str(tr)[:1500])
except Exception as e:
print(" TaskResult:", type(tr), str(tr)[:500])
except Exception as e:
print(" 查询异常:", e)
print("\n========== 完成 ==========")
print(" 访问: http://42.194.245.239:8090 8080 被 frps 占用,已改用 8090")
print(" 宝塔 Docker 总览点击「刷新容器列表」可见 lytiao-www")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,78 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""仅启动 lytiao 容器(不重新构建),并返回详细输出"""
import base64, json, os, re, sys, time
CKB_ID, REGION = "ins-ciyv2mxa", "ap-guangzhou"
def _cred():
d = os.path.dirname(os.path.abspath(__file__))
for _ in range(6):
r = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md")
if os.path.isfile(r):
t = open(r, encoding="utf-8").read()
sid = skey = None
in_t = False
for L in t.splitlines():
if "### 腾讯云" in L: in_t = True
elif in_t and L.strip().startswith("###"): break
elif in_t:
m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I)
if m and m.group(1).strip().startswith("AKID"): sid = m.group(1).strip()
m = re.search(r"SecretKey[^|]*\|\s*`([^`]+)`", L, re.I)
if m: skey = m.group(1).strip()
return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY")
d = os.path.dirname(d)
return None, None
# 只启动,不构建;若镜像不存在会报错,便于定位
CMD = """cd /opt/lytiao_docker
sed -i 's/8080:80/8090:80/g' docker-compose.yml
docker compose down 2>/dev/null
docker compose up -d 2>&1
echo "---"
docker ps -a --filter name=lytiao
echo "---"
curl -sI -o /dev/null -w 'HTTP: %{http_code}' http://127.0.0.1:8090/ 2>/dev/null || echo "curl fail"
"""
def main():
sid, skey = _cred()
if not sid or not skey: print("❌ 无凭证"); return 1
from tencentcloud.common import credential
from tencentcloud.tat.v20201028 import tat_client, models
cred = credential.Credential(sid, skey)
client = tat_client.TatClient(cred, REGION)
req = models.RunCommandRequest()
req.Content = base64.b64encode(CMD.encode()).decode()
req.InstanceIds = [CKB_ID]
req.CommandType, req.Timeout = "SHELL", 120
req.CommandName = "CKB_lytiao_Start"
r = client.RunCommand(req)
print("已下发,等待 90s...")
time.sleep(90)
req2 = models.DescribeInvocationTasksRequest()
f = models.Filter(); f.Name, f.Values = "invocation-id", [r.InvocationId]
req2.Filters = [f]
r2 = client.DescribeInvocationTasks(req2)
for t in (r2.InvocationTaskSet or []):
print("状态:", getattr(t, "TaskStatus", ""))
tr = getattr(t, "TaskResult", None)
if tr:
try:
j = json.loads(tr) if isinstance(tr, str) else (tr if hasattr(tr, "get") else vars(tr))
o = j.get("Output", "") if isinstance(j, dict) else ""
e = j.get("Error", "") if isinstance(j, dict) else ""
if o:
try: o = base64.b64decode(o).decode("utf-8", errors="replace")
except: pass
print("\n--- 输出 ---\n", o)
if e:
try: e = base64.b64decode(e).decode("utf-8", errors="replace")
except: pass
print("\n--- 错误 ---\n", e)
except Exception as ex:
print("解析异常:", ex, "| raw:", str(tr)[:600])
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""快速检查 lytiao 容器状态,获取 TAT 输出"""
import base64, json, os, re, sys, time
CKB_ID, REGION = "ins-ciyv2mxa", "ap-guangzhou"
def _cred():
d = os.path.dirname(os.path.abspath(__file__))
for _ in range(6):
r = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md")
if os.path.isfile(r):
t = open(r, encoding="utf-8").read()
sid = skey = None
in_t = False
for L in t.splitlines():
if "### 腾讯云" in L: in_t = True
elif in_t and L.strip().startswith("###"): break
elif in_t:
m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I)
if m and m.group(1).strip().startswith("AKID"): sid = m.group(1).strip()
m = re.search(r"SecretKey[^|]*\|\s*`([^`]+)`", L, re.I)
if m: skey = m.group(1).strip()
return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY")
d = os.path.dirname(d)
return None, None
CMD = """cd /opt/lytiao_docker 2>/dev/null && docker compose ps -a
echo "---"
docker ps -a --filter name=lytiao
echo "---"
ss -tlnp | grep -E '8080|8090' || true
echo "---"
curl -sI -o /dev/null -w '8090: %{http_code}' http://127.0.0.1:8090/ 2>/dev/null || echo "8090 curl fail"
"""
def main():
sid, skey = _cred()
if not sid or not skey: print("❌ 无凭证"); return 1
from tencentcloud.common import credential
from tencentcloud.tat.v20201028 import tat_client, models
cred = credential.Credential(sid, skey)
client = tat_client.TatClient(cred, REGION)
req = models.RunCommandRequest()
req.Content = base64.b64encode(CMD.encode()).decode()
req.InstanceIds = [CKB_ID]
req.CommandType, req.Timeout = "SHELL", 30
req.CommandName = "CKB_lytiao_Check"
r = client.RunCommand(req)
inv = r.InvocationId
print("⏳ 等待 25s...")
time.sleep(25)
req2 = models.DescribeInvocationTasksRequest()
f = models.Filter(); f.Name, f.Values = "invocation-id", [inv]
req2.Filters = [f]
r2 = client.DescribeInvocationTasks(req2)
for t in (r2.InvocationTaskSet or []):
print("状态:", getattr(t, "TaskStatus", ""))
tr = getattr(t, "TaskResult", None)
if tr:
try:
j = tr if hasattr(tr, "get") else (json.loads(tr) if isinstance(tr, str) else vars(tr) if hasattr(tr, "__dict__") else {})
o = (j.get("Output", "") if isinstance(j, dict) else "") or getattr(tr, "Output", "")
if o:
try:
o = base64.b64decode(o).decode("utf-8", errors="replace")
except Exception:
pass
print("\n--- 服务器输出 ---\n", o)
except Exception as e:
print("解析:", e, "raw:", str(tr)[:300])
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
存客宝 lytiao 快速部署 - 使用精简 Dockerfile不编译扩展2 分钟内完成)
"""
import base64, json, os, re, sys, time
CKB_ID, REGION = "ins-ciyv2mxa", "ap-guangzhou"
def _cred():
d = os.path.dirname(os.path.abspath(__file__))
for _ in range(6):
r = os.path.join(d, "运营中枢", "工作台", "00_账号与API索引.md")
if os.path.isfile(r):
t = open(r, encoding="utf-8").read()
sid = skey = None
in_t = False
for L in t.splitlines():
if "### 腾讯云" in L: in_t = True
elif in_t and L.strip().startswith("###"): break
elif in_t:
m = re.search(r"SecretId[^|]*\|\s*`([^`]+)`", L, re.I)
if m and m.group(1).strip().startswith("AKID"): sid = m.group(1).strip()
m = re.search(r"SecretKey[^|]*\|\s*`([^`]+)`", L, re.I)
if m: skey = m.group(1).strip()
return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY")
d = os.path.dirname(d)
return None, None
# 精简 Dockerfile不编译扩展拉取即用
CMD = """set -e
DIR="/opt/lytiao_docker"
SRC="/www/wwwroot/www.lytiao.com"
mkdir -p "$DIR"
cat > "$DIR/Dockerfile" << 'EOF'
FROM php:7.1-apache
RUN a2enmod rewrite
WORKDIR /var/www/html
EXPOSE 80
EOF
cat > "$DIR/docker-compose.yml" << 'EOF'
services:
lytiao-web:
build: .
container_name: lytiao-www
ports:
- "8090:80"
volumes:
- ./www:/var/www/html
restart: unless-stopped
EOF
rm -rf "$DIR/www"
cp -a "$SRC" "$DIR/www"
cd "$DIR"
docker compose down 2>/dev/null || true
docker compose up -d --build 2>&1
sleep 3
docker ps -a --filter name=lytiao
curl -sI -o /dev/null -w 'HTTP: %{http_code}' http://127.0.0.1:8090/ 2>/dev/null || echo "fail"
"""
def main():
sid, skey = _cred()
if not sid or not skey: print("❌ 无凭证"); return 1
from tencentcloud.common import credential
from tencentcloud.tat.v20201028 import tat_client, models
cred = credential.Credential(sid, skey)
client = tat_client.TatClient(cred, REGION)
req = models.RunCommandRequest()
req.Content = base64.b64encode(CMD.encode()).decode()
req.InstanceIds = [CKB_ID]
req.CommandType, req.Timeout = "SHELL", 180
req.CommandName = "CKB_lytiao_Quick"
r = client.RunCommand(req)
print("已下发(精简构建,约 2 分钟InvocationId:", r.InvocationId)
print("等待 150s 获取结果...")
time.sleep(150)
req2 = models.DescribeInvocationTasksRequest()
f = models.Filter(); f.Name, f.Values = "invocation-id", [r.InvocationId]
req2.Filters = [f]
r2 = client.DescribeInvocationTasks(req2)
for t in (r2.InvocationTaskSet or []):
print("状态:", getattr(t, "TaskStatus", ""))
err_info = getattr(t, "ErrorInfo", None)
if err_info:
print("ErrorInfo:", err_info)
tr = getattr(t, "TaskResult", None)
print("TaskResult type:", type(tr))
if tr:
try:
j = json.loads(tr) if isinstance(tr, str) else (tr if hasattr(tr, "get") else {})
o = j.get("Output", "") if isinstance(j, dict) else ""
e = j.get("Error", "") if isinstance(j, dict) else ""
if o:
try: o = base64.b64decode(o).decode("utf-8", errors="replace")
except: pass
print("\n--- 输出 ---\n", o)
if e:
try: e = base64.b64decode(e).decode("utf-8", errors="replace")
except: pass
print("\n--- 错误 ---\n", e)
except Exception as ex:
print("解析异常:", ex)
print("TaskResult repr:", repr(tr)[:1200])
print("\n访问: http://42.194.245.239:8090")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
腾讯云 API 为存客宝放行 8090lytiao Docker 容器端口)
"""
import sys
import os
# 复用 443 脚本逻辑,仅端口改为 8090
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
_CKB_IP = "42.194.245.239"
_REGIONS = ["ap-guangzhou", "ap-beijing", "ap-shanghai"]
def _find_root():
d = _SCRIPT_DIR
for _ in range(6):
if os.path.basename(d) == "卡若AI" or (os.path.isdir(os.path.join(d, "运营中枢")) and os.path.isdir(os.path.join(d, "01_卡资"))):
return d
d = os.path.dirname(d)
return None
def _read_creds():
root = _find_root()
if not root:
return None, None
path = os.path.join(root, "运营中枢", "工作台", "00_账号与API索引.md")
if not os.path.isfile(path):
return None, None
import re
with open(path, "r", encoding="utf-8") as f:
text = f.read()
sid = skey = None
in_t = False
for line in text.splitlines():
if "### 腾讯云" in line:
in_t = True
continue
if in_t and line.strip().startswith("###"):
break
if not in_t:
continue
m = re.search(r"\|\s*[^|]*(?:SecretId|密钥)[^|]*\|\s*`([^`]+)`", line, re.I)
if m and m.group(1).strip().startswith("AKID"):
sid = m.group(1).strip()
m = re.search(r"\|\s*SecretKey\s*\|\s*`([^`]+)`", line, re.I)
if m:
skey = m.group(1).strip()
return sid or os.environ.get("TENCENTCLOUD_SECRET_ID"), skey or os.environ.get("TENCENTCLOUD_SECRET_KEY")
def main():
secret_id, secret_key = _read_creds()
if not secret_id or not secret_key:
print("❌ 未配置腾讯云 SecretId/SecretKey")
return 1
from tencentcloud.common import credential
from tencentcloud.cvm.v20170312 import cvm_client, models as cvm_models
from tencentcloud.vpc.v20170312 import vpc_client, models as vpc_models
cred = credential.Credential(secret_id, secret_key)
sg_ids, region = [], None
for r in _REGIONS:
try:
c = cvm_client.CvmClient(cred, r)
req = cvm_models.DescribeInstancesRequest()
req.Limit = 100
resp = c.DescribeInstances(req)
for ins in (getattr(resp, "InstanceSet", None) or []):
if _CKB_IP in list(getattr(ins, "PublicIpAddresses", None) or []):
sg_ids = list(getattr(ins, "SecurityGroupIds", None) or [])
region = r
break
except Exception:
continue
if sg_ids:
break
if not sg_ids or not region:
print("❌ 存客宝 %s 未找到" % _CKB_IP)
return 1
print(" 存客宝安全组放行 8090lytiao Docker")
vc = vpc_client.VpcClient(cred, region)
added = 0
for sg_id in sg_ids:
try:
req = vpc_models.CreateSecurityGroupPoliciesRequest()
req.SecurityGroupId = sg_id
policy_set = vpc_models.SecurityGroupPolicySet()
ing = vpc_models.SecurityGroupPolicy()
ing.Protocol = "TCP"
ing.Port = "8090"
ing.CidrBlock = "0.0.0.0/0"
ing.Action = "ACCEPT"
ing.PolicyDescription = "lytiao-Docker"
policy_set.Ingress = [ing]
req.SecurityGroupPolicySet = policy_set
vc.CreateSecurityGroupPolicies(req)
print("%s 已添加 8090/TCP" % sg_id)
added += 1
except Exception as e:
err = str(e)
if "RuleAlreadyExists" in err or "已存在" in err or "duplicate" in err.lower():
print("%s 8090 规则已存在" % sg_id)
added += 1
else:
print("%s: %s" % (sg_id, e))
if added > 0:
print(" 放行完成,可访问 http://42.194.245.239:8090")
return 0
if __name__ == "__main__":
sys.exit(main())