Known 侧所有服务端进程(营销站 Website / 后台全栈宿主 KnownWeb)都按同一份 Linux 部署约定:
dotnet publish 目录产物 + systemd 单元 + Nginx 反代。Website 已交付这套脚本,
KnownWeb 沿用同一模式。
一键流程
# 开发机(Windows / PowerShell)
cd Website
pwsh deploy/build.ps1 # 出 deploy/artifacts/known-website-Release-linux-x64-2026...tar.gz
scp deploy/artifacts/*.tar.gz user@host:/tmp/
# 服务器(Linux)
/tmp/install.sh /tmp/known-website-Release-linux-x624-xxx.tar.gz
install.sh 内部做的事:解包到 /var/www/known-website.new → 冒烟起临时进程 curl 200 →
原子 mv 切换 → systemctl restart known-website → 线上再打一次 /robots.txt 探活。
失败保留 .old 供回滚。
systemd 单元要点
deploy/systemd/known-website.service:
Type=notify— .NET 10 内建 systemd readiness 通知,比simple更准确;User=knownwww— 非 root;Environment=ASPNETCORE_URLS=http://127.0.0.1:5000— 只监听 loopback,不暴露公网;Environment=ASPNETCORE_FORWARDEDHEADERS_ENABLED=1— 读 Nginx 传上来的X-Forwarded-*;KillSignal=SIGINT TimeoutStopSec=25— 优雅关闭、给应用 25s 收尾;- 硬化:
ProtectSystem=strict PrivateTmp NoNewPrivileges RestrictNamespaces LockPersonality RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6; - 例外:
MemoryDenyWriteExecute=false(.NET JIT 需要 W^X 例外,别照抄网上模板); ReadWritePaths=/var/log/known-website— 只有这一个可写目录,其余全ReadOnly。
Nginx 反代要点
deploy/nginx/known-website.conf:
- HTTP 段:
server_name www.known.org.cn;return 301 https://$host$request_uri;例外放行/.well-known/acme-challenge/到 webroot 供 certbot; - HTTPS 段:
listen 443 ssl http2;TLS 1.2/1.3、HSTSmax-age=31536000; includeSubDomains; - 三段缓存策略:
- 指纹静态资源(
site.<hash>.css//_framework/*):immutable, max-age=31536000; - SEO 端点(
/robots.txt/sitemap.xml/llms.txt):max-age=3600; - HTML:
no-cache。
- 指纹静态资源(
- 透传上游:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; - gzip 6、CSP 保守(
default-src 'self'+ Google Fonts + 内联 pre-paint 脚本unsafe-inline)。
Docker 备选
营销站根下 Dockerfile 是多阶段:sdk:10.0 构建 → aspnet:10.0 运行,非 root $APP_UID=10001、
HEALTHCHECK curl /。deploy/docker-compose.yml 加了 read_only + cap_drop ALL + no-new-privileges + tmpfs /tmp。
海外发布(Blazor SSR 不能扔 Cloudflare Pages 那种纯静态托管)建议: Fly.io / Workers for Containers / 便宜 VPS + 这份 Dockerfile。
环境变量与 profile
营销站的子域配置走 Site__* 环境变量(详见 Website/docs/deploy-topology.md §5):
Site__Canonical=https://www.known.org.cn
Site__App=https://app.known.org.cn
Site__Api=https://api.known.org.cn
Site__Play=https://play.known.org.cn
Site__Gitee=https://gitee.com/known/Known
绑定优先级:命令行 --Site:Canonical > 环境变量 Site__Canonical > appsettings.Production.json
appsettings.json。切 profile 不换产物、只换 env。
Site__DocsSite__Status已从配置移除——docs 与 status 走营销站内子树,不再是独立子域。
冒烟清单
上线前后各跑一次:
BASE=https://www.known.org.cn
for p in / /features /pricing /templates /docs /docs/getting-started /docs/concepts \
/docs/guides /docs/faq /docs/changelog /roadmap /status \
/about /contact /legal /legal/terms /legal/privacy /legal/license /legal/sla \
/robots.txt /sitemap.xml /llms.txt /not-found; do
printf '%s ' "$(curl -s -o /dev/null -w '%{http_code}' "$BASE$p")"; echo "$p"
done
# 期望:前 19 页 200;SEO 三端点 200;/not-found 也 200(Router.NotFoundPage 命中)
# 另测 /nope-random 应 404(UseStatusCodePagesWithReExecute 保持 404 状态码)
备案与合规
- 国内直投
.org.cn域名需 ICP 备案;页脚已挂苏ICP备20045379号-3 · 公安备案。 - 海外 profile 若走非
.org.cn域,需要在Footer.razor判Site.Canonical是否含.org.cn决定是否渲染备案链接(当前留白,未实装)。 - Cookie 同意条:本站只签发严格必要 Cookie(会话 / CSRF 防护),无分析或追踪类 Cookie,多数法域豁免同意,不需要 GDPR/Cookie 弹窗。
常见坑
MemoryDenyWriteExecute=true— 抄网上模板把这个开了,.NET JIT 立即崩、进程起不来。Known 的 systemd 单元已明确留例外。- Nginx 忘了
proxy_http_version 1.1— 上游连接每次重建、性能差;单元里默认已配。 install.sh冒烟打的是 5099 而不是 5000 — 冒烟阶段临时端口,与 systemd 生产端口不冲突;改脚本时别把两处混一起。/var/log/known-website属主不对 —journalctl之外若要落文件日志,ReadWritePaths里的目录chown knownwww:knownwww。
相关
- 完整部署包与文档:
Website/deploy/README.md、Website/docs/deploy-topology.md。 - 后台全栈宿主
KnownWeb(同进程 serve WASM 客户端 +[WebApi]端点 + Cookie 鉴权)部署与上面同构,套用同一份 systemd + Nginx 约定即可。
扫码关注公众号
扫码加入 QQ 群