本页目录

wildwindstudio.com 接入 EdgeOne(EO)运维手册

站点 zone:wildwindstudio.com = zone-31ojren22baa(腾讯云 EO,个人版 plan-personal,dnsPodAccess 接入,DNS 记录在 DNSPod) 覆盖两类操作:① 把后端 API 域名反代进 EO;② 把前端静态站用 COS 桶 + EO 托管。


0. 凭证地图(脱敏)

凭证用途值存放位置
主账号密钥⁠(appid 1258895932,标 tencent_sms)管 EO / DNSPod / COS / CAM,本手册所有管理操作都用它项目 resflow/config.toml[tencent_sms](勿入库)
tencent_email 密钥另一个账号(appid 1392608203),⁠与本 zone 无关同上 [tencent_email]
前端上传凭证CAM 子用户 cos_op(Uin 100049168554),前端 coscli 上传 + EO 刷缓存前端侧持有;策略见 §4
CLB 源站证书(certbot)EO→CLB 回源那一段的证书bj-middleware-1,见另一手册《wildwindstudio.com 证书自动续签》

⚠️ tccli 调 teo 写操作必须带 --endpoint teo.tencentcloudapi.com,否则默认会解析到国际站 teo.intl.tencentcloudapi.com 连接超时。 ⚠️ tccli 没有 cos 服务⁠、机器上无 coscli/SDK;COS 操作用手写 HMAC-SHA1 签名 + python requests 直调 XML API(脚本见 §5)。

设置管理凭证到环境变量(后续命令假设已 export):

# 从 config.toml 的 [tencent_sms] 读取,避免明文粘贴
cd <resflow 项目目录>
eval "$(python3 - <<'PY'
import re
t=open('config.toml').read()
m=re.search(r'\[tencent_sms\](.*?)(\n\[|\Z)', t, re.S).group(1)
sid=re.search(r'secret_id\s*=\s*"([^"]+)"',m).group(1)
sk=re.search(r'secret_key\s*=\s*"([^"]+)"',m).group(1)
print(f'export TENCENTCLOUD_SECRET_ID={sid}; export TENCENTCLOUD_SECRET_KEY={sk}')
PY
)"

1. 场景 A:把后端 API 域名反代进 EO

cn-reserve-api.wildwindstudio.com 为例(回源腾讯云 CLB)。

场景 A:API 反代请求链路 客户端 HTTPS EO 边缘 终止 TLS · CC/WAF 基础防护 回源 HTTPS:443 HostHeader 透传原域名 CLB lb-mm70ra69 443 监听器 · L7 host 规则 k8s 两段 HTTPS:客户端→EO 边缘证书终止,EO→CLB 回源仍是 HTTPS:443(源站证书由 certbot 续签), HostHeader 透传让 CLB 按域名转发进 k8s;新 host 无独立策略时默认回落站点级 ZoneDefaultPolicy 防护。

步骤⁠(顺序:先建回源+证书,等证书 deployed 再切 DNS,避免空窗 5xx):

ZONE=zone-31ojren22baa
DOMAIN=cn-reserve-api.wildwindstudio.com
CLB=lb-mm70ra69-ihl1vfoqucp93sma.clb.bj-tencentclb.com

# ① 建加速域名(IP_DOMAIN 回源到 CLB,强制 HTTPS:443;CLB 的 80 是强制 302 跳转,别回源到 80)
tccli teo CreateAccelerationDomain --region ap-guangzhou --endpoint teo.tencentcloudapi.com \
  --ZoneId $ZONE --DomainName $DOMAIN \
  --OriginInfo '{"OriginType":"IP_DOMAIN","Origin":"'$CLB'","PrivateAccess":"off"}' \
  --OriginProtocol HTTPS --HttpsOriginPort 443
# 返回 OwnershipVerification=null 即无需归属校验(子域从 zone 继承)

# ② 开边缘免费证书(EO 自动签发 + 自动续期)
tccli teo ModifyHostsCertificate --region ap-guangzhou --endpoint teo.tencentcloudapi.com \
  --ZoneId $ZONE --Hosts '["'$DOMAIN'"]' --Mode eofreecert

# 轮询直到 certStatus=deployed(约 4~10 分钟)
tccli teo DescribeAccelerationDomains --region ap-guangzhou --endpoint teo.tencentcloudapi.com \
  --ZoneId $ZONE | python3 -c "import json,sys;d=json.load(sys.stdin);[print(a['DomainStatus'],(a.get('Certificate') or {}).get('List',[{}])[0].get('Status')) for a in d['AccelerationDomains'] if a['DomainName']=='$DOMAIN']"

# ③ 证书 deployed 后,改 DNSPod 里该记录指向 EO 入口 <domain>.eo.dnse1.com
#    —— 找到 RecordId 后用 ModifyRecord(只改这一条)
tccli dnspod DescribeRecordList --endpoint dnspod.tencentcloudapi.com \
  --Domain wildwindstudio.com --Subdomain cn-reserve-api           # 拿 RecordId
tccli dnspod ModifyRecord --endpoint dnspod.tencentcloudapi.com \
  --Domain wildwindstudio.com --RecordId <RECORD_ID> \
  --SubDomain cn-reserve-api --RecordType CNAME --RecordLine 默认 \
  --Value cn-reserve-api.wildwindstudio.com.eo.dnse1.com --TTL 600

验证⁠(切 DNS 前可用 EO 边缘 IP 预检):

IP=$(dig +short @119.29.29.29 cn-reserve-api.wildwindstudio.com.eo.dnse1.com A | grep -E '^[0-9]' | head -1)
curl -s -o /dev/null -w "HTTP %{http_code} TLS %{ssl_verify_result}\n" \
  --resolve cn-reserve-api.wildwindstudio.com:443:$IP https://cn-reserve-api.wildwindstudio.com/openapi

防护⁠:新 host 无独立域名级策略时,回落到站点级 ZoneDefaultPolicy(CC 自适应频控 on/Loose/Challenge + 托管 WAF on/DetectionOnly)。这是基础防护,未配精细限速。查看: tccli teo DescribeSecurityPolicy ... --Entity ZoneDefaultPolicy

注意 CC 动作是 JSChallenge,非浏览器客户端(如游戏客户端直连 API)触发时解不了挑战;Loose 下仅极端异常才触发,可接受。


2. 场景 B:COS 静态前端 + EO 托管

cn-reservation.wildwindstudio.com 为例,桶 cn-reservation-webui-1258895932(ap-nanjing)。参照桶 game-reserve-webui-1258895932(ap-tokyo,reservation 站)。

场景 B:COS 静态前端请求链路 客户端 HTTPS EO 边缘 回源(FOLLOW) COS 静态网站端点 <bucket>.cos-website.<region>.myqcloud.com OriginProtocol=FOLLOW,回源到 COS 静态网站端点(bucket-website 域名);与场景 A 不同, COS 源站不能传 HostHeader,EO 会自动填,手动指定反而报错。

步骤⁠:

# ① 建桶(公有读)+ ② 开静态网站(index.html + AutoAddressing)
#    —— 用 §5 的 cos_admin.py(手写签名),或控制台操作
python3 cos_admin.py create-bucket cn-reservation-webui-1258895932 ap-nanjing

# ③ EO 建加速域名(COS 回源;OriginType=COS 时【不能】传 HostHeader,EO 自动填)
ZONE=zone-31ojren22baa
WEP=cn-reservation-webui-1258895932.cos-website.ap-nanjing.myqcloud.com
tccli teo CreateAccelerationDomain --region ap-guangzhou --endpoint teo.tencentcloudapi.com \
  --ZoneId $ZONE --DomainName cn-reservation.wildwindstudio.com \
  --OriginInfo '{"OriginType":"COS","Origin":"'$WEP'","PrivateAccess":"off"}' \
  --OriginProtocol FOLLOW --HttpOriginPort 80 --HttpsOriginPort 443

# ④ 开边缘证书(同场景 A ②)
tccli teo ModifyHostsCertificate --region ap-guangzhou --endpoint teo.tencentcloudapi.com \
  --ZoneId $ZONE --Hosts '["cn-reservation.wildwindstudio.com"]' --Mode eofreecert

# ⑤ DNS:全新子域用 CreateRecord(不动任何现有记录)
tccli dnspod CreateRecord --endpoint dnspod.tencentcloudapi.com \
  --Domain wildwindstudio.com --SubDomain cn-reservation \
  --RecordType CNAME --RecordLine 默认 \
  --Value cn-reservation.wildwindstudio.com.eo.dnse1.com --TTL 600

验证⁠(空桶回源应返回 COS 404,证明链路通;传了 index.html 即 200):

DOM=cn-reservation.wildwindstudio.com
IP=$(dig +short @119.29.29.29 $DOM A | grep -E '^[0-9]' | head -1)
echo | openssl s_client -connect $IP:443 -servername $DOM 2>/dev/null | openssl x509 -noout -subject
# subject=CN=<域名> 即边缘证书已铺好(未铺好时先服 *.cdn.myqcloud.com 默认证书、TLS 握不上)
curl -s -o /dev/null -w "HTTP %{http_code}\n" --resolve $DOM:443:$IP https://$DOM/

3. 前端部署(coscli 上传 + EO 刷缓存)

前端两件套(每个站一份,改 bucket / 改 purge 目标域名即可):

coscli 配置⁠(cn-reservation-coscli.yaml):

cos:
  base:
    secretid: <cos_op 的 SecretId,脱敏>
    secretkey: <cos_op 的 SecretKey,脱敏>
    sessiontoken: ""
    protocol: https
  buckets:
  - name: cn-reservation-webui-1258895932     # 换新桶
    alias: website
    region: ap-nanjing                          # 换区域
    endpoint: cos.ap-nanjing.myqcloud.com       # 换 endpoint
    ofs: false

刷缓存脚本⁠(TC3 签名调 teo CreatePurgeTask,改新站只需改 Targets 域名):

export TENCENTCLOUD_SECRET_ID=<cos_op SecretId>
export TENCENTCLOUD_SECRET_KEY=<cos_op SecretKey>
# payload 里 Targets 改成目标域名:
# {"ZoneId":"*","Type":"purge_host","Targets":["cn-reservation.wildwindstudio.com"]}
./cn-reservation-purge.sh

发布流程:coscli sync <构建产物目录> cos://website/ -c cn-reservation-coscli.yaml → 跑刷缓存脚本。


4. 前端上传凭证的 CAM 授权(新桶必做)

前端子用户 cos_op 的权限由单条自定义策略 cos_full_permission(PolicyId 274095472)控制。 每上一个新桶,都要把新桶资源加进这条策略的 cos:* 语句⁠,否则 coscli 上传报 403 AccessDenied。 (teo:CreatePurgeTask 已授权 *,purge 任意域名不用再动。)

# 读当前策略
tccli cam GetPolicy --PolicyId 274095472

# 更新(保留旧桶资源,追加新桶资源;resource 格式 qcs::cos:<region>:uid/<appid>:<bucket-appid>/*)
tccli cam UpdatePolicy --PolicyId 274095472 --PolicyDocument '{
  "version":"2.0",
  "statement":[
    {"action":["cos:*"],"effect":"allow","resource":[
      "qcs::cos:ap-tokyo:uid/1258895932:game-reserve-webui-1258895932/*",
      "qcs::cos:ap-nanjing:uid/1258895932:cn-reservation-webui-1258895932/*"
    ]},
    {"action":["teo:CreatePurgeTask"],"effect":"allow","resource":["*"]}
  ]
}'

定位子用户:cam ListUsers → 对候选 cam ListAccessKeys --TargetUin <uin> 匹配 AK → cam ListAttachedUserPolicies --TargetUin <uin> 找策略。


5. 附:COS 管理脚本(手写签名,无需 coscli)

本机无 coscli/SDK 时,用 python requests + COS HMAC-SHA1 签名直调 XML API。核心签名函数:

import time, hmac, hashlib, requests
def cos_sign(SID, SKEY, method, host, uri='/', headers=None, params=None):
    headers=headers or {'host':host}; params=params or {}
    now=int(time.time()); kt=f"{now};{now+600}"
    sk=hmac.new(SKEY.encode(),kt.encode(),hashlib.sha1).hexdigest()
    hl=';'.join(sorted(k.lower() for k in headers)); pl=';'.join(sorted(k.lower() for k in params))
    enc=lambda d:'&'.join(f"{k.lower()}={requests.utils.quote(str(d[k]),safe='')}" for k in sorted(d,key=str.lower))
    fmt=f"{method.lower()}\n{uri}\n{enc(params)}\n{enc(headers)}\n"
    sts=f"sha1\n{kt}\n{hashlib.sha1(fmt.encode()).hexdigest()}\n"
    sig=hmac.new(sk.encode(),sts.encode(),hashlib.sha1).hexdigest()
    return (f"q-sign-algorithm=sha1&q-ak={SID}&q-sign-time={kt}&q-key-time={kt}"
            f"&q-header-list={hl}&q-url-param-list={pl}&q-signature={sig}")
  • GET Service(列所有桶):host service.cos.myqcloud.com,GET /
  • PUT Bucket + 公有读⁠:host <bucket>.cos.<region>.myqcloud.com,PUT /,签并发 x-cos-acl: public-read
  • PUT 静态网站⁠:PUT /?website,body 见下,需 content-md5(base64(md5(body)))+ content-type: application/xml
  • 验证⁠:GET /?acl、GET /?website

静态网站 body(与参照桶一致):

<WebsiteConfiguration>
  <IndexDocument><Suffix>index.html</Suffix></IndexDocument>
  <AutoAddressing><Status>Enabled</Status></AutoAddressing>
</WebsiteConfiguration>

6. 常见坑速查

现象原因 / 处理
teo.intl... timed outtccli teo 写操作没带 --endpoint teo.tencentcloudapi.com
UnSupportChangeHostHeaderWithThisOriginTypeOriginType=COS 时不能传 HostHeader,去掉即可(EO 自动填)
切 DNS 后 TLS 握手失败、HTTP 000边缘证书未铺开,边缘先服 *.cdn.myqcloud.com;等 certStatus=deployed 且边缘 subject=本域名(几分钟~几十分钟)
coscli 上传 403 AccessDeniedcos_op 策略没加该桶资源,见 §4
DomainStatus 长时间 processEO 加速域名 provisioning 中,不影响已 deployed 的证书功能;会自动翻 online
DescribeDnsRecords(teo) 返回 0 条dnsPodAccess 模式 DNS 记录在 DNSPod,用 dnspod DescribeRecordList

附:本 zone 现有加速域名

域名回源用途
cn-reserve-api.wildwindstudio.comCLB lb-mm70ra69(HTTPS:443)国服后端 API
cn-reservation.wildwindstudio.comCOS cn-reservation-webui(ap-nanjing)国服预约前端
reservation / meltdown / wwwCOS 静态桶日服/官网前端