<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Xiobb&#039;s Blog</title><link>https://www.imxb.de/</link><atom:link href="https://www.imxb.de/rss/post.asp" rel="self" type="application/rss+xml" /><language>zh-CN</language><description>My everything.</description><lastBuildDate>Mon, 17 Aug 2026 14:19:00 +0800</lastBuildDate><pubDate>Mon, 17 Aug 2026 14:19:00 +0800</pubDate><item><title>AI复盘：国内网络环境、低配置，“半裸机”部署最新版Sentry？</title><link>https://www.imxb.de/new_archives/69.shtml</link><guid>https://www.imxb.de/new_archives/69.shtml</guid><pubDate>Mon, 17 Aug 2026 14:19:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[众所周知，Sentry出奇地吃内存：官方要求最低配置 16G内存+16G SWAP。实测也确实如此，16G内存可以轻松吃满，而这来自与他所依赖的大量内存杀手服务。此外，官方的安装脚本并不适配国内...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>众所周知，Sentry出奇地吃内存：官方要求最低配置 16G内存+16G SWAP。实测也确实如此，16G内存可以轻松吃满，而这来自与他所依赖的大量内存杀手服务。此外，官方的安装脚本并不适配国内网络环境，安装很难进行下去。</p><p>通过将部分软件直接裸机运行，手动安装Sentry，可以全部走国内镜像站来达到最佳下载速度，虽然确实麻烦了点。它依赖的软件实在太多。最后大概跑了二十多个容器，其余服务全部都在宿主机直接启动，否则docker内保底60+容器，带来极大的内存开销。</p><p>这边使用配置 8核8G 100G硬盘，开启10G SWAP（其实安装的时候是5G，装完了再改的），顺利启动了服务。<del>和你gpt5.6老师奋战到凌晨两点多</del></p><p>先上图，剩下的时间交给gpt5.6。</p><p><img src="https://data.imxb.de/pic/i/2026/08/17/62FB6408-92D5-412C-9EDC-B7F83934C977.png" alt="image" title="image"><br><img src="https://data.imxb.de/pic/i/2026/08/17/EC4643F0-14CB-4818-A357-3573ED5B9C3A.png" alt="image" title="image"></p><h1>gpt5.6时间  （超长文jing'gao）</h1><blockquote><p><strong>部署目标</strong></p><p>在一台 Debian 13 单机上部署 Sentry Self-Hosted <code>26.7.2</code>，启用 <code>feature-complete</code> 完整功能模式：</p><ul><li>PostgreSQL、PgBouncer、Redis、Memcached、Kafka、ClickHouse、SeaweedFS：<strong>宿主机原生运行</strong></li><li>Sentry Web、Relay、Snuba、Consumer、Symbolicator 等：<strong>Docker 运行</strong></li><li>Envoy：<strong>宿主机原生运行，作为唯一 HTTP 入口</strong></li><li>不使用 Nginx</li><li>不使用 Docker PostgreSQL、Docker Redis、Docker Kafka、Docker ClickHouse</li></ul><p><strong>推荐资源</strong></p><pre><code class="lang-text">最低：4 核 CPU、8 GB RAM、100 GB SSD
建议：4~8 核 CPU、16 GB RAM、200 GB SSD</code></pre><p>Sentry <code>feature-complete</code> 包含 Kafka、ClickHouse、Snuba 和多个 Consumer；2 核 4GB 的机器通常不适合完整模式。</p></blockquote><hr><h2>一、最终架构与端口</h2><p>架构如下：</p><pre><code class="lang-text">                           ┌──────────────────┐
                           │ 浏览器 / SDK / CDN │
                           └────────┬─────────┘
                                    │ HTTP :80
                         ┌──────────▼──────────┐
                         │ Envoy（宿主机原生） │
                         └─────────┬───────────┘
                                   │
             ┌─────────────────────┴─────────────────────┐
             │                                           │
 ┌───────────▼────────────┐                 ┌────────────▼──────────┐
 │ Sentry Web :9001       │                 │ Relay :3000           │
 │ Docker / host network  │                 │ Docker / host network │
 └───────────┬────────────┘                 └────────────┬──────────┘
             │                                           │
             └───────────────────┬───────────────────────┘
                                 │ 127.0.0.1
 ┌───────────────────────────────┼─────────────────────────────────────┐
 │ PostgreSQL / PgBouncer / Redis / Memcached / Kafka / ClickHouse     │
 │ SeaweedFS                                                              │
 │                              宿主机原生运行                            │
 └─────────────────────────────────────────────────────────────────────┘</code></pre><p>本方案所有 Docker 应用服务使用：</p><pre><code class="lang-yaml">network_mode: host</code></pre><p>因此容器内访问宿主机服务时用：</p><pre><code class="lang-text">127.0.0.1</code></pre><h3>端口表</h3><table><thead><tr><th>服务</th><th align="right">监听端口</th><th>用途</th><th>是否公开</th></tr></thead><tbody><tr><td>Envoy</td><td align="right"><code>80</code></td><td>浏览器、SDK、CDN 入口</td><td>是</td></tr><tr><td>Envoy Admin</td><td align="right"><code>9901</code></td><td>Envoy 状态接口</td><td>仅本机</td></tr><tr><td>Sentry Web</td><td align="right"><code>9001</code></td><td>Sentry 管理后台上游</td><td>仅内部</td></tr><tr><td>Relay</td><td align="right"><code>3000</code></td><td>事件接收上游</td><td>仅内部</td></tr><tr><td>Snuba API</td><td align="right"><code>1218</code></td><td>Snuba 查询 API</td><td>仅内部</td></tr><tr><td>Symbolicator</td><td align="right"><code>3021</code></td><td>符号和 Source Map 解析</td><td>仅内部</td></tr><tr><td>PostgreSQL</td><td align="right"><code>5432</code></td><td>数据库</td><td>仅本机</td></tr><tr><td>PgBouncer</td><td align="right"><code>6432</code></td><td>PostgreSQL 连接池</td><td>仅本机</td></tr><tr><td>Redis</td><td align="right"><code>6379</code></td><td>缓存、Session、队列</td><td>仅本机</td></tr><tr><td>Memcached</td><td align="right"><code>11211</code></td><td>缓存</td><td>仅本机</td></tr><tr><td>Kafka</td><td align="right"><code>9092</code></td><td>消息队列</td><td>仅本机</td></tr><tr><td>ClickHouse TCP</td><td align="right"><code>9000</code></td><td>Snuba 数据存储</td><td>仅本机</td></tr><tr><td>ClickHouse HTTP</td><td align="right"><code>8123</code></td><td>ClickHouse HTTP API</td><td>仅本机</td></tr><tr><td>SeaweedFS S3</td><td align="right"><code>8333</code></td><td>S3 兼容对象存储</td><td>仅本机</td></tr></tbody></table><p>为什么 Sentry Web 采用 <code>9001</code>？</p><pre><code class="lang-text">ClickHouse Native TCP 默认使用 9000
Sentry Web 默认也使用 9000
Docker Host Network 下两者会端口冲突</code></pre><p>所以：</p><pre><code class="lang-text">ClickHouse：9000
Sentry Web：9001</code></pre><p>最终访问入口始终是：</p><pre><code class="lang-text">http://服务器IP/</code></pre><p>例如：</p><pre><code class="lang-text">http://10.0.10.3/</code></pre><p>而不是：</p><pre><code class="lang-text">http://10.0.10.3:9001/</code></pre><hr><h1>二、系统基础准备</h1><p>以下命令以 <code>root</code> 身份执行。</p><p>更新系统：</p><pre><code class="lang-bash">apt update
apt upgrade -y</code></pre><p>安装常用工具、AWS CLI、数据库客户端：</p><pre><code class="lang-bash">apt install -y \
  ca-certificates \
  curl \
  wget \
  gnupg \
  lsb-release \
  unzip \
  jq \
  vim \
  git \
  awscli \
  memcached \
  redis-server \
  pgbouncer \
  postgresql \
  postgresql-client</code></pre><p>检查系统资源：</p><pre><code class="lang-bash">free -h
df -h
nproc</code></pre><p>设置时区，例如中国大陆：</p><pre><code class="lang-bash">timedatectl set-timezone Asia/Shanghai
timedatectl status</code></pre><p>为 Kafka、ClickHouse 等高文件句柄服务设置限制：</p><pre><code class="lang-bash">cat &gt;/etc/security/limits.d/99-sentry.conf &lt;&lt;&#039;EOF&#039;
* soft nofile 262144
* hard nofile 262144
EOF</code></pre><hr><h1>三、安装 Docker Engine 与 Compose Plugin</h1><p>如果系统中已经安装 Docker，可跳过本节。</p><p>清理 Debian 自带旧包：</p><pre><code class="lang-bash">apt remove -y \
  docker.io \
  docker-doc \
  docker-compose \
  podman-docker \
  containerd \
  runc 2&gt;/dev/null || true</code></pre><p>添加 Docker 官方仓库密钥：</p><pre><code class="lang-bash">install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/debian/gpg \
  -o /etc/apt/keyrings/docker.asc

chmod a+r /etc/apt/keyrings/docker.asc</code></pre><p>添加仓库：</p><pre><code class="lang-bash">cat &gt;/etc/apt/sources.list.d/docker.sources &lt;&lt;EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release &amp;&amp; echo &quot;$VERSION_CODENAME&quot;)
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF</code></pre><p>安装：</p><pre><code class="lang-bash">apt update

apt install -y \
  docker-ce \
  docker-ce-cli \
  containerd.io \
  docker-buildx-plugin \
  docker-compose-plugin</code></pre><p>启动并验证：</p><pre><code class="lang-bash">systemctl enable --now docker

docker version
docker compose version</code></pre><hr><h1>四、配置 PostgreSQL 和 PgBouncer</h1><p>Sentry 使用 PostgreSQL 保存用户、组织、项目、事件元数据和系统配置。</p><p>PgBouncer 用来管理连接池。Sentry 连接 PgBouncer，而不是直接连接 PostgreSQL。</p><pre><code class="lang-text">PostgreSQL：127.0.0.1:5432
PgBouncer：127.0.0.1:6432
Sentry：连接 127.0.0.1:6432</code></pre><h2>4.1 启动 PostgreSQL</h2><p>查看集群：</p><pre><code class="lang-bash">pg_lsclusters</code></pre><p>期望看到类似：</p><pre><code class="lang-text">Ver Cluster Port Status Owner    Data directory
17  main    5432 online postgres /var/lib/postgresql/17/main</code></pre><p>若未启动：</p><pre><code class="lang-bash">systemctl enable --now postgresql
systemctl start postgresql
pg_lsclusters</code></pre><p>测试 PostgreSQL：</p><pre><code class="lang-bash">sudo -u postgres \
  psql \
  -p 5432 \
  -d postgres \
  -c &#039;SELECT version();&#039;</code></pre><h2>4.2 创建数据库与账号</h2><p>进入 PostgreSQL：</p><pre><code class="lang-bash">sudo -u postgres psql</code></pre><p>执行：</p><pre><code class="lang-sql">CREATE USER sentry WITH PASSWORD &#039;替换为强数据库密码&#039;;

CREATE DATABASE sentry
  OWNER sentry
  ENCODING &#039;UTF8&#039;
  TEMPLATE template0;</code></pre><p>退出：</p><pre><code class="lang-sql">\q</code></pre><p>测试 <code>sentry</code> 用户直连：</p><pre><code class="lang-bash">PGPASSWORD=&#039;替换为强数据库密码&#039; \
psql \
  -h 127.0.0.1 \
  -p 5432 \
  -U sentry \
  -d sentry \
  -c &#039;SELECT current_database(), current_user;&#039;</code></pre><p>预期：</p><pre><code class="lang-text"> current_database | current_user
------------------+-------------
 sentry           | sentry</code></pre><h2>4.3 配置 PgBouncer</h2><p>编辑配置：</p><pre><code class="lang-bash">vim /etc/pgbouncer/pgbouncer.ini</code></pre><p>可使用以下单机本机连接配置：</p><pre><code class="lang-ini">[databases]
sentry = host=127.0.0.1 port=5432 dbname=sentry

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432

auth_type = trust

pool_mode = transaction

max_client_conn = 10000
default_pool_size = 50
min_pool_size = 5
reserve_pool_size = 10

server_reset_query = DISCARD ALL

admin_users = postgres,sentry
stats_users = postgres,sentry

ignore_startup_parameters = extra_float_digits,options

log_connections = 1
log_disconnections = 1
log_pooler_errors = 1</code></pre><p>启动：</p><pre><code class="lang-bash">systemctl enable --now pgbouncer
systemctl restart pgbouncer</code></pre><p>检查端口：</p><pre><code class="lang-bash">ss -lntp | grep &#039;:6432&#039;</code></pre><p>验证 Sentry 通过 PgBouncer 访问数据库：</p><pre><code class="lang-bash">PGPASSWORD=&#039;替换为强数据库密码&#039; \
psql \
  -h 127.0.0.1 \
  -p 6432 \
  -U sentry \
  -d sentry \
  -c &#039;SELECT current_database(), current_user;&#039;</code></pre><blockquote>此处 <code>auth_type = trust</code> 只适用于 PgBouncer 严格绑定 <code>127.0.0.1</code> 的本机单机部署。不要把 <code>6432</code> 暴露给公网。</blockquote><hr><h1>五、Redis 与 Memcached</h1><h2>5.1 Redis</h2><p>编辑配置：</p><pre><code class="lang-bash">vim /etc/redis/redis.conf</code></pre><p>确认：</p><pre><code class="lang-conf">bind 127.0.0.1 -::1
protected-mode yes

appendonly yes

maxmemory 0
maxmemory-policy volatile-lru</code></pre><p>启动：</p><pre><code class="lang-bash">systemctl enable --now redis-server
systemctl restart redis-server</code></pre><p>验证：</p><pre><code class="lang-bash">redis-cli ping</code></pre><p>预期：</p><pre><code class="lang-text">PONG</code></pre><h2>5.2 Memcached</h2><p>编辑：</p><pre><code class="lang-bash">vim /etc/memcached.conf</code></pre><p>确认绑定本地：</p><pre><code class="lang-text">-l 127.0.0.1
-p 11211</code></pre><p>启动：</p><pre><code class="lang-bash">systemctl enable --now memcached
systemctl restart memcached</code></pre><p>验证：</p><pre><code class="lang-bash">ss -lntp | grep &#039;:11211&#039;</code></pre><hr><h1>六、部署 Kafka 单节点 KRaft</h1><p>Sentry 26.7.2 官方 Docker Compose 使用 Kafka：</p><pre><code class="lang-text">confluentinc/cp-kafka:7.6.6</code></pre><p>无 Docker 的 Kafka 安装方式依赖你选择的发行包；但关键配置必须保证：</p><ul><li>单节点 KRaft；</li><li>Kafka 监听 <code>127.0.0.1:9092</code>；</li><li>单节点副本系数为 <code>1</code>；</li><li>单机环境可临时开启自动建 Topic。</li></ul><p>关键配置文件假定为：</p><pre><code class="lang-text">/etc/kafka/server.properties</code></pre><p>核心配置如下：</p><pre><code class="lang-properties">process.roles=broker,controller
node.id=1

controller.quorum.voters=1@127.0.0.1:9093
controller.listener.names=CONTROLLER

listeners=PLAINTEXT://127.0.0.1:9092,CONTROLLER://127.0.0.1:9093
advertised.listeners=PLAINTEXT://127.0.0.1:9092

listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
inter.broker.listener.name=PLAINTEXT

log.dirs=/var/lib/kafka

num.partitions=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

log.retention.hours=24

message.max.bytes=50000000
replica.fetch.max.bytes=50000000

auto.create.topics.enable=true</code></pre><p>创建目录和服务用户：</p><pre><code class="lang-bash">useradd \
  --system \
  --home /var/lib/kafka \
  --shell /usr/sbin/nologin \
  kafka || true

mkdir -p /var/lib/kafka /var/log/kafka

chown -R kafka:kafka /var/lib/kafka /var/log/kafka</code></pre><p>生成 KRaft Cluster ID：</p><pre><code class="lang-bash">kafka-storage.sh random-uuid</code></pre><p>格式化数据目录：</p><pre><code class="lang-bash">kafka-storage.sh format \
  -t &#039;替换为上一步生成的ClusterID&#039; \
  -c /etc/kafka/server.properties</code></pre><p>启动：</p><pre><code class="lang-bash">systemctl enable --now kafka
systemctl restart kafka</code></pre><p>验证：</p><pre><code class="lang-bash">ss -lntp | grep &#039;:9092&#039;

kafka-topics.sh \
  --bootstrap-server 127.0.0.1:9092 \
  --list</code></pre><blockquote><p>初始化阶段，Sentry 可能报：</p><pre><code class="lang-text">Timeout when waiting for Kafka topic &#039;ingest-occurrences&#039;
KafkaError UNKNOWN_TOPIC_OR_PART</code></pre><p>对单机初次部署，<code>auto.create.topics.enable=true</code> 可以帮助初始化。长期生产运行建议显式创建 Topic，并管理分区、保留时间和磁盘容量。</p></blockquote><hr><h1>七、部署 ClickHouse</h1><p>ClickHouse 是 Snuba 的底层事件分析数据库。</p><p>Sentry 26.7.2 官方 Compose 使用的基线是：</p><pre><code class="lang-text">altinity/clickhouse-server:25.3.6.10034.altinitystable</code></pre><p>建议优先使用和该版本接近、经过验证的 ClickHouse 版本。使用更高版本可能遇到 Snuba Migration 兼容问题。</p><ul><li>这里AI没写，上下文压缩过没了，我是自己搜教程安装的当前最新版本。</li></ul><p>启动后验证：</p><pre><code class="lang-bash">systemctl enable --now clickhouse-server
systemctl restart clickhouse-server

clickhouse-client --query &#039;SELECT version();&#039;
clickhouse-client --query &#039;SELECT 1;&#039;</code></pre><p>确认端口：</p><pre><code class="lang-bash">ss -lntp | grep -E &#039;:(8123|9000)\b&#039;</code></pre><h2>7.1 Snuba Migration 兼容处理</h2><p>如果 Snuba 初始化中看到：</p><pre><code class="lang-text">Column(s) retention_days ...
AggregatingMergeTree ...
allow_dimensions_outside_sorting_key</code></pre><p>创建兼容配置：</p><pre><code class="lang-bash">cat &gt;/etc/clickhouse-server/config.d/sentry-snuba-compat.xml &lt;&lt;&#039;EOF&#039;
&lt;clickhouse&gt;
  &lt;merge_tree&gt;
    &lt;allow_dimensions_outside_sorting_key&gt;1&lt;/allow_dimensions_outside_sorting_key&gt;
  &lt;/merge_tree&gt;
&lt;/clickhouse&gt;
EOF</code></pre><p>重启：</p><pre><code class="lang-bash">systemctl restart clickhouse-server</code></pre><p>验证：</p><pre><code class="lang-bash">clickhouse-client --query &#039;SELECT 1;&#039;</code></pre><p>不要把该项写在错误层级，否则会看到：</p><pre><code class="lang-text">UNKNOWN_SETTING allow_dimensions_outside_sorting_key</code></pre><hr><h1>八、部署 SeaweedFS，并用 AWS CLI 创建 Bucket</h1><p>SeaweedFS 提供 S3 Compatible API，可用于 Sentry Profiles 等对象存储。</p><h2>8.1 下载 SeaweedFS</h2><p>以实际版本下载 URL 为准。下载完成后放到：</p><pre><code class="lang-text">/usr/local/bin/weed</code></pre><p>并设置权限：</p><pre><code class="lang-bash">chmod 755 /usr/local/bin/weed

weed version</code></pre><h2>8.2 创建目录与服务用户</h2><pre><code class="lang-bash">useradd \
  --system \
  --home /var/lib/seaweedfs \
  --shell /usr/sbin/nologin \
  seaweedfs || true

mkdir -p /var/lib/seaweedfs
mkdir -p /etc/seaweedfs

chown -R seaweedfs:seaweedfs /var/lib/seaweedfs
chmod 750 /var/lib/seaweedfs</code></pre><h2>8.3 创建 S3 用户凭据</h2><p>生成 Secret：</p><pre><code class="lang-bash">openssl rand -base64 36</code></pre><p>创建 SeaweedFS S3 身份文件：</p><pre><code class="lang-bash">vim /etc/seaweedfs/s3.json</code></pre><p>示例：</p><pre><code class="lang-json">{
  &quot;identities&quot;: [
    {
      &quot;name&quot;: &quot;sentry&quot;,
      &quot;credentials&quot;: [
        {
          &quot;accessKey&quot;: &quot;sentry&quot;,
          &quot;secretKey&quot;: &quot;替换为生成的随机Secret&quot;
        }
      ],
      &quot;actions&quot;: [
        &quot;Admin&quot;,
        &quot;Read&quot;,
        &quot;Write&quot;,
        &quot;List&quot;,
        &quot;Tagging&quot;
      ]
    }
  ]
}</code></pre><p>保护配置：</p><pre><code class="lang-bash">chown root:seaweedfs /etc/seaweedfs/s3.json
chmod 640 /etc/seaweedfs/s3.json</code></pre><h2>8.4 创建 systemd 服务</h2><p>创建：</p><pre><code class="lang-bash">vim /etc/systemd/system/seaweedfs.service</code></pre><p>内容：</p><pre><code class="lang-ini">[Unit]
Description=SeaweedFS for Sentry
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=seaweedfs
Group=seaweedfs

Environment=AWS_ACCESS_KEY_ID=sentry
Environment=AWS_SECRET_ACCESS_KEY=替换为与s3.json一致的Secret

ExecStart=/usr/local/bin/weed mini \
  -dir=/var/lib/seaweedfs \
  -filer.defaultReplicaPlacement=000 \
  -metricsPort=9091 \
  -volume.dir.idx=/var/lib/seaweedfs/idx \
  -volume.index=leveldbLarge \
  -volume.preStopSeconds=8 \
  -volume.readMode=redirect \
  -ip.bind=127.0.0.1 \
  -webdav=false

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target</code></pre><p>启动：</p><pre><code class="lang-bash">systemctl daemon-reload
systemctl enable --now seaweedfs
systemctl status seaweedfs --no-pager -l</code></pre><p>验证：</p><pre><code class="lang-bash">curl -fsS http://127.0.0.1:8333/healthz
ss -lntp | grep &#039;:8333&#039;</code></pre><h2>8.5 使用 AWS CLI 创建 Sentry Bucket</h2><p>安装 <code>awscli</code> 后，临时写入 S3 凭据：</p><pre><code class="lang-bash">export AWS_ACCESS_KEY_ID=&#039;sentry&#039;
export AWS_SECRET_ACCESS_KEY=&#039;替换为SeaweedFS Secret&#039;
export AWS_DEFAULT_REGION=&#039;us-east-1&#039;</code></pre><p>测试 SeaweedFS S3 API：</p><pre><code class="lang-bash">aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 ls</code></pre><p>创建 Sentry Profile Bucket：</p><pre><code class="lang-bash">aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 mb s3://profiles</code></pre><p>确认创建成功：</p><pre><code class="lang-bash">aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 ls</code></pre><p>应该可看到：</p><pre><code class="lang-text">profiles</code></pre><p>也可以用 <code>s3api</code> 验证：</p><pre><code class="lang-bash">aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3api list-buckets</code></pre><p>测试写入与读取：</p><pre><code class="lang-bash">echo &#039;sentry seaweedfs test&#039; &gt;/tmp/sentry-s3-test.txt

aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 cp \
  /tmp/sentry-s3-test.txt \
  s3://profiles/sentry-s3-test.txt

aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 ls \
  s3://profiles/

aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 cp \
  s3://profiles/sentry-s3-test.txt \
  /tmp/sentry-s3-test-downloaded.txt

cat /tmp/sentry-s3-test-downloaded.txt</code></pre><p>完成后清理 Shell 环境变量：</p><pre><code class="lang-bash">unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_DEFAULT_REGION</code></pre><hr><h1>九、下载并准备 Sentry 26.7.2</h1><pre><code class="lang-bash">mkdir -p /opt
cd /opt

wget \
  https://github.com/getsentry/self-hosted/archive/refs/tags/26.7.2.tar.gz

tar -xzf 26.7.2.tar.gz

cd /opt/self-hosted-26.7.2</code></pre><p>设置完整功能模式：</p><pre><code class="lang-bash">vim .env</code></pre><p>确认：</p><pre><code class="lang-dotenv">COMPOSE_PROFILES=feature-complete</code></pre><hr><h1>十、创建 Sentry 配置</h1><p>创建目录：</p><pre><code class="lang-bash">mkdir -p \
  /etc/sentry \
  /etc/sentry/relay \
  /etc/sentry/symbolicator</code></pre><p>复制官方基础配置：</p><pre><code class="lang-bash">cp /opt/self-hosted-26.7.2/sentry/config.example.yml \
  /etc/sentry/config.yml

cp /opt/self-hosted-26.7.2/sentry/sentry.conf.example.py \
  /etc/sentry/sentry.conf.py</code></pre><h2>10.1 配置数据库和 Web 端口</h2><p>编辑：</p><pre><code class="lang-bash">vim /etc/sentry/sentry.conf.py</code></pre><p>数据库配置应为：</p><pre><code class="lang-python">DATABASES = {
    &quot;default&quot;: {
        &quot;ENGINE&quot;: &quot;sentry.db.postgres&quot;,
        &quot;NAME&quot;: &quot;sentry&quot;,
        &quot;USER&quot;: &quot;sentry&quot;,
        &quot;PASSWORD&quot;: &quot;替换为数据库密码&quot;,
        &quot;HOST&quot;: &quot;127.0.0.1&quot;,
        &quot;PORT&quot;: &quot;6432&quot;,
    }
}</code></pre><p>配置 Web：</p><pre><code class="lang-python">SENTRY_WEB_HOST = &quot;0.0.0.0&quot;
SENTRY_WEB_PORT = 9001</code></pre><h2>10.2 配置内部和外部 URL、S3 Profile 存储</h2><p>编辑：</p><pre><code class="lang-bash">vim /etc/sentry/config.yml</code></pre><p>加入：</p><pre><code class="lang-yaml">system.internal-url-prefix: &#039;http://127.0.0.1:9001&#039;
system.url-prefix: &#039;http://10.0.10.3&#039;</code></pre><p>SeaweedFS S3 配置：</p><pre><code class="lang-yaml">filestore.profiles-backend: &#039;s3&#039;

filestore.profiles-options:
  bucket_acl: &quot;private&quot;
  default_acl: &quot;private&quot;

  access_key: &quot;sentry&quot;
  secret_key: &quot;替换为SeaweedFS实际Secret&quot;

  bucket_name: &quot;profiles&quot;
  region_name: &quot;us-east-1&quot;

  endpoint_url: &quot;http://127.0.0.1:8333&quot;
  addressing_style: &quot;path&quot;
  signature_version: &quot;s3v4&quot;</code></pre><p>Symbolicator：</p><pre><code class="lang-yaml">symbolicator.enabled: true

symbolicator.options:
  url: &quot;http://127.0.0.1:3021&quot;</code></pre><h2>10.3 创建运行环境文件</h2><p>生成两个随机密钥：</p><pre><code class="lang-bash">openssl rand -base64 48
openssl rand -base64 48</code></pre><p>创建：</p><pre><code class="lang-bash">vim /etc/sentry/sentry.env</code></pre><p>内容：</p><pre><code class="lang-dotenv">SENTRY_CONF=/etc/sentry

COMPOSE_PROFILES=feature-complete

SENTRY_EVENT_RETENTION_DAYS=90

SENTRY_KAFKA_MAX_POLL_INTERVAL_MS=300000

SNUBA=http://127.0.0.1:1218

SENTRY_SYSTEM_SECRET_KEY=替换为第一个随机密钥

LAUNCHPAD_RPC_SHARED_SECRET=替换为第二个随机密钥</code></pre><hr><h1>十一、Relay 和 Symbolicator 配置</h1><p>Relay：</p><pre><code class="lang-bash">vim /etc/sentry/relay/config.yml</code></pre><pre><code class="lang-yaml">relay:
  upstream: &quot;http://127.0.0.1:9001/&quot;
  host: 127.0.0.1
  port: 3000

processing:
  enabled: true

  kafka_config:
    - {name: &quot;bootstrap.servers&quot;, value: &quot;127.0.0.1:9092&quot;}

  redis: redis://127.0.0.1:6379</code></pre><p>Symbolicator：</p><pre><code class="lang-bash">vim /etc/sentry/symbolicator/config.yml</code></pre><pre><code class="lang-yaml">cache_dir: &quot;/data&quot;

bind: &quot;127.0.0.1:3021&quot;

logging:
  level: &quot;warn&quot;

sentry_dsn: null</code></pre><p>配置保护：</p><pre><code class="lang-bash">chown -R root:root /etc/sentry

find /etc/sentry -type d -exec chmod 755 {} \;
find /etc/sentry -type f -exec chmod 644 {} \;

chmod 600 \
  /etc/sentry/sentry.env \
  /etc/sentry/sentry.conf.py \
  /etc/sentry/config.yml \
  /etc/sentry/relay/config.yml \
  /etc/sentry/symbolicator/config.yml</code></pre><hr><h1>十二、创建“应用层专用”Docker Compose</h1><p>原始官方 <code>docker-compose.yml</code> 同时定义了：</p><pre><code class="lang-text">PostgreSQL
Redis
Kafka
ClickHouse
SeaweedFS
Memcached
Sentry
Snuba
Relay
...</code></pre><p>本方案不能直接用它 <code>up -d</code>，否则会重新启动一套 Docker 基础设施。</p><p>需要创建：</p><pre><code class="lang-text">/opt/self-hosted-26.7.2/docker-compose.app.yml</code></pre><p>原则如下：</p><ol><li><p><strong>不保留</strong>基础服务：</p><pre><code class="lang-text">postgres
pgbouncer
redis
memcached
kafka
clickhouse
seaweedfs
nginx</code></pre></li><li><p><strong>保留</strong>应用服务：</p><pre><code class="lang-text">web
relay
symbolicator
symbolicator-cleanup
snuba-api

events-consumer
attachments-consumer
post-process-forwarder-errors

snuba-errors-consumer
snuba-outcomes-consumer
snuba-outcomes-billing-consumer
snuba-transactions-consumer
snuba-replays-consumer
snuba-metrics-consumer
snuba-group-attributes-consumer
snuba-issue-occurrence-consumer
snuba-subscription-consumer-events
snuba-replacer

taskbroker
taskworker
taskscheduler
vroom
launchpad-taskworker
uptime-checker</code></pre></li><li><p>所有保留服务增加：</p><pre><code class="lang-yaml">network_mode: host</code></pre></li><li>删除 <code>depends_on</code> 中对 Docker 基础服务的依赖；</li><li><p>把原先 Docker DNS 服务名改为宿主机地址：</p><pre><code class="lang-text">postgres      → 127.0.0.1
pgbouncer     → 127.0.0.1
redis         → 127.0.0.1
kafka         → 127.0.0.1
clickhouse    → 127.0.0.1
seaweedfs     → 127.0.0.1
symbolicator  → 127.0.0.1
snuba-api     → 127.0.0.1
vroom         → 127.0.0.1</code></pre></li><li>用 <code>/etc/sentry</code> 覆盖官方容器配置目录。</li></ol><p>例如，Sentry 默认环境中的：</p><pre><code class="lang-yaml">SNUBA: &quot;http://snuba-api:1218&quot;
VROOM: &quot;http://vroom:8085&quot;</code></pre><p>改为：</p><pre><code class="lang-yaml">SNUBA: &quot;http://127.0.0.1:1218&quot;
VROOM: &quot;http://127.0.0.1:8085&quot;</code></pre><p>Snuba 默认环境中的：</p><pre><code class="lang-yaml">CLICKHOUSE_HOST: clickhouse
DEFAULT_BROKERS: &quot;kafka:9092&quot;
REDIS_HOST: redis</code></pre><p>改为：</p><pre><code class="lang-yaml">CLICKHOUSE_HOST: 127.0.0.1
DEFAULT_BROKERS: &quot;127.0.0.1:9092&quot;
REDIS_HOST: 127.0.0.1</code></pre><h2>12.1 Compose 核心示例</h2><p>下面不是全部 Consumer 定义，但展示关键改动方式：</p><pre><code class="lang-yaml">services:
  web:
    image: sentry-self-hosted-local:26.7.2
    build:
      context: ./sentry
      args:
        - SENTRY_IMAGE
    network_mode: host
    restart: unless-stopped
    env_file:
      - /etc/sentry/sentry.env
    environment:
      SENTRY_CONF: /etc/sentry
      SNUBA: http://127.0.0.1:1218
      VROOM: http://127.0.0.1:8085
    volumes:
      - sentry-data:/data
      - /etc/sentry:/etc/sentry:ro
      - ./geoip:/geoip:ro
      - ./certificates:/usr/local/share/ca-certificates:ro
    entrypoint: /etc/sentry/entrypoint.sh
    command: [&quot;run&quot;, &quot;web&quot;]

  relay:
    image: ghcr.io/getsentry/relay:26.7.2
    network_mode: host
    restart: unless-stopped
    env_file:
      - /etc/sentry/sentry.env
    volumes:
      - /etc/sentry/relay:/work/.relay
    command: [&quot;run&quot;]

  snuba-api:
    image: ghcr.io/getsentry/snuba:26.7.2
    network_mode: host
    restart: unless-stopped
    environment:
      SNUBA_SETTINGS: self_hosted
      CLICKHOUSE_HOST: 127.0.0.1
      DEFAULT_BROKERS: 127.0.0.1:9092
      REDIS_HOST: 127.0.0.1
      UWSGI_MAX_REQUESTS: &quot;10000&quot;
      UWSGI_DISABLE_LOGGING: &quot;true&quot;
    command: api --host 0.0.0.0 --port 1218

  symbolicator:
    image: ghcr.io/getsentry/symbolicator:26.7.2
    network_mode: host
    restart: unless-stopped
    volumes:
      - symbolicator-data:/data
      - /etc/sentry/symbolicator:/etc/symbolicator:ro
    command: run -c /etc/symbolicator/config.yml

volumes:
  sentry-data:
  symbolicator-data:</code></pre><blockquote>创建完整 <code>docker-compose.app.yml</code> 时，应以当前版本官方 <code>docker-compose.yml</code> 中各服务的 <code>command</code>、环境变量和挂载为准，保留完整功能所需 Consumer；不要自己猜测 Consumer 命令。</blockquote><p>构建本地 Sentry 镜像：</p><pre><code class="lang-bash">cd /opt/self-hosted-26.7.2

docker compose \
  -f docker-compose.app.yml \
  build web</code></pre><p>启动：</p><pre><code class="lang-bash">docker compose \
  -f docker-compose.app.yml \
  up -d</code></pre><p>查看状态：</p><pre><code class="lang-bash">docker ps --format &#039;table {{.Names}}\t{{.Status}}&#039;</code></pre><hr><h1>十三、初始化 Sentry</h1><p>基础服务正常后，执行：</p><pre><code class="lang-bash">cd /opt/self-hosted-26.7.2

docker compose \
  -f docker-compose.app.yml \
  run --rm web upgrade --create-kafka-topics</code></pre><p>初始化会执行：</p><pre><code class="lang-text">PostgreSQL Migration
Snuba / ClickHouse Migration
Kafka Topic 创建
管理员账号创建
Sentry 初始化配置</code></pre><p>完成后：</p><pre><code class="lang-bash">docker compose \
  -f docker-compose.app.yml \
  up -d</code></pre><hr><h1>十四、使用 Envoy 反向代理</h1><p>Envoy 是唯一公网入口。</p><p>路由规则：</p><pre><code class="lang-text">/api/store/                 → Relay :3000
/api/&lt;数字项目ID&gt;/...        → Relay :3000
/api/0/relays/              → Relay :3000
/_assets/...                → Sentry Web :9001，改写静态资源路径
其他所有请求               → Sentry Web :9001</code></pre><p>创建目录：</p><pre><code class="lang-bash">mkdir -p /etc/envoy /var/log/envoy</code></pre><p>创建配置：</p><pre><code class="lang-bash">vim /etc/envoy/envoy.yaml</code></pre><p>内容：</p><pre><code class="lang-yaml">static_resources:
  listeners:
    - name: sentry_http
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 80

      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                &quot;@type&quot;: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager

                stat_prefix: sentry_http

                use_remote_address: true
                xff_num_trusted_hops: 0

                route_config:
                  name: sentry_routes

                  virtual_hosts:
                    - name: sentry
                      domains:
                        - &quot;*&quot;

                      routes:
                        - match:
                            prefix: &quot;/api/store/&quot;
                          route:
                            cluster: relay
                            timeout: 90s

                        - match:
                            safe_regex:
                              google_re2: {}
                              regex: &quot;^/api/[0-9]+/.*&quot;
                          route:
                            cluster: relay
                            timeout: 90s

                        - match:
                            prefix: &quot;/api/0/relays/&quot;
                          route:
                            cluster: relay
                            timeout: 90s

                        - match:
                            prefix: &quot;/_assets/&quot;
                          route:
                            cluster: sentry_web
                            prefix_rewrite: &quot;/_static/dist/sentry/&quot;
                            timeout: 90s

                        - match:
                            prefix: &quot;/&quot;
                          route:
                            cluster: sentry_web
                            timeout: 90s

                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      &quot;@type&quot;: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
    - name: sentry_web
      type: STATIC
      connect_timeout: 5s
      lb_policy: ROUND_ROBIN

      load_assignment:
        cluster_name: sentry_web
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 127.0.0.1
                      port_value: 9001

    - name: relay
      type: STATIC
      connect_timeout: 5s
      lb_policy: ROUND_ROBIN

      load_assignment:
        cluster_name: relay
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 127.0.0.1
                      port_value: 3000

admin:
  access_log_path: /var/log/envoy/admin_access.log

  address:
    socket_address:
      address: 127.0.0.1
      port_value: 9901</code></pre><p>创建 systemd 服务：</p><pre><code class="lang-bash">vim /etc/systemd/system/envoy.service</code></pre><pre><code class="lang-ini">[Unit]
Description=Envoy Proxy for Sentry
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/envoy -c /etc/envoy/envoy.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target</code></pre><p>验证 Envoy 配置：</p><pre><code class="lang-bash">/usr/local/bin/envoy \
  -c /etc/envoy/envoy.yaml \
  --mode validate</code></pre><p>启动：</p><pre><code class="lang-bash">systemctl daemon-reload
systemctl enable --now envoy

systemctl status envoy --no-pager -l</code></pre><p>检查监听：</p><pre><code class="lang-bash">ss -lntp | grep -E &#039;:(80|9901)\b&#039;</code></pre><hr><h1>十五、上线前逐层验证</h1><p>不要只看 Docker 是否显示 <code>Up</code>。必须逐层检查。</p><h2>15.1 PostgreSQL</h2><pre><code class="lang-bash">pg_lsclusters</code></pre><p>期望：</p><pre><code class="lang-text">17  main  5432 online</code></pre><pre><code class="lang-bash">sudo -u postgres \
  psql \
  -p 5432 \
  -d postgres \
  -c &#039;SELECT 1;&#039;</code></pre><h2>15.2 PgBouncer</h2><pre><code class="lang-bash">PGPASSWORD=&#039;数据库密码&#039; \
psql \
  -h 127.0.0.1 \
  -p 6432 \
  -U sentry \
  -d sentry \
  -c &#039;SELECT 1;&#039;</code></pre><h2>15.3 Redis</h2><pre><code class="lang-bash">redis-cli ping</code></pre><p>必须返回：</p><pre><code class="lang-text">PONG</code></pre><h2>15.4 ClickHouse</h2><pre><code class="lang-bash">clickhouse-client --query &#039;SELECT 1;&#039;</code></pre><h2>15.5 SeaweedFS 和 Bucket</h2><pre><code class="lang-bash">curl -fsS http://127.0.0.1:8333/healthz</code></pre><pre><code class="lang-bash">export AWS_ACCESS_KEY_ID=&#039;sentry&#039;
export AWS_SECRET_ACCESS_KEY=&#039;SeaweedFS Secret&#039;
export AWS_DEFAULT_REGION=&#039;us-east-1&#039;

aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3 ls</code></pre><p>应看到：</p><pre><code class="lang-text">profiles</code></pre><h2>15.6 Sentry Web</h2><p>健康检查：</p><pre><code class="lang-bash">curl -v --max-time 10 \
  http://127.0.0.1:9001/_health/</code></pre><p>预期：</p><pre><code class="lang-text">HTTP/1.1 200 OK

ok</code></pre><p>首页：</p><pre><code class="lang-bash">curl -v --max-time 15 \
  http://127.0.0.1:9001/</code></pre><p>预期：</p><pre><code class="lang-text">HTTP/1.1 302 Found
location: /auth/login/</code></pre><h2>15.7 Envoy</h2><pre><code class="lang-bash">curl -v --max-time 10 \
  http://127.0.0.1/_health/</code></pre><p>预期：</p><pre><code class="lang-text">HTTP/1.1 200 OK
server: envoy

ok</code></pre><p>测试首页：</p><pre><code class="lang-bash">curl -v --max-time 15 \
  http://127.0.0.1/</code></pre><p>预期：</p><pre><code class="lang-text">HTTP/1.1 302 Found
location: /auth/login/
server: envoy</code></pre><p>查看上游状态：</p><pre><code class="lang-bash">curl -fsS http://127.0.0.1:9901/clusters \
  | grep -E -A8 -B2 &#039;sentry_web|relay&#039;</code></pre><p>重点确认：</p><pre><code class="lang-text">health_flags::healthy</code></pre><hr><h1>十六、复盘：这次部署中最值得记录的坑</h1><h2>16.1 <code>/_health/</code> 返回 200，不代表 Sentry 页面能用</h2><p>这是最容易误判的地方。</p><p>健康检查：</p><pre><code class="lang-bash">curl http://127.0.0.1/_health/</code></pre><p>可能返回：</p><pre><code class="lang-text">ok</code></pre><p>但首页仍然一直转圈，Envoy 最终显示：</p><pre><code class="lang-text">upstream request timeout</code></pre><p>日志中出现：</p><pre><code class="lang-text">connection to server at &quot;127.0.0.1&quot;, port 6432 failed:
FATAL: client_login_timeout (server down)</code></pre><p>说明：</p><pre><code class="lang-text">Envoy 正常
Sentry Web 存活
/_health/ 正常
PostgreSQL / PgBouncer 链路异常</code></pre><p>原因：</p><pre><code class="lang-text">/_health/ 不需要访问数据库
/          需要数据库、Session、认证和组织信息</code></pre><p>所以真正的检查顺序应是：</p><pre><code class="lang-bash">pg_lsclusters

PGPASSWORD=&#039;数据库密码&#039; \
psql -h 127.0.0.1 -p 6432 -U sentry -d sentry -c &#039;SELECT 1;&#039;

curl -v --max-time 15 http://127.0.0.1:9001/

curl -v --max-time 15 http://127.0.0.1/</code></pre><h2>16.2 Sentry Web 和 ClickHouse 都不能用 9000</h2><p>如果 Sentry Web 仍配置为：</p><pre><code class="lang-text">9000</code></pre><p>而 ClickHouse 已经占用：</p><pre><code class="lang-text">9000</code></pre><p>浏览器可能看到 ClickHouse 相关响应，而不是 Sentry。</p><p>正确规划：</p><pre><code class="lang-text">ClickHouse：9000
Sentry Web：9001
Envoy：80</code></pre><h2>16.3 访问 <code>:9001</code> 不是正确的用户访问方式</h2><p>用户只能访问：</p><pre><code class="lang-text">http://服务器IP/</code></pre><p>即 Envoy <code>80</code>。</p><p><code>9001</code> 是 Sentry Web 的内部上游端口。即使它可访问，也不应作为最终入口、SDK 地址或 CDN 回源地址。</p><h2>16.4 Kafka Topic 初始化超时</h2><p>典型报错：</p><pre><code class="lang-text">Timeout when waiting for Kafka topic &#039;ingest-occurrences&#039;
KafkaError UNKNOWN_TOPIC_OR_PART</code></pre><p>检查：</p><pre><code class="lang-bash">systemctl status kafka --no-pager -l

kafka-topics.sh \
  --bootstrap-server 127.0.0.1:9092 \
  --list</code></pre><p>新部署中可临时启用：</p><pre><code class="lang-properties">auto.create.topics.enable=true</code></pre><p>随后重新执行：</p><pre><code class="lang-bash">docker compose \
  -f docker-compose.app.yml \
  run --rm web upgrade --create-kafka-topics</code></pre><h2>16.5 ClickHouse 版本与 Snuba Migration</h2><p>使用更高版本 ClickHouse 时，Snuba 可能出现：</p><pre><code class="lang-text">allow_dimensions_outside_sorting_key</code></pre><p>可通过前文 <code>&lt;merge_tree&gt;</code> 配置暂时兼容，但长期更推荐使用接近官方 <code>26.7.2</code> 支持范围的 ClickHouse 版本。</p><h2>16.6 Redis 不正常会连锁影响 Web</h2><p>Sentry Web 可能报：</p><pre><code class="lang-text">Error 111 connecting to 127.0.0.1:6379. Connection refused</code></pre><p>此时先执行：</p><pre><code class="lang-bash">redis-cli ping</code></pre><p>没有得到：</p><pre><code class="lang-text">PONG</code></pre><p>之前，不必继续怀疑 Envoy 或浏览器。</p><h2>16.7 Symbolicator 未定义 StatsD 环境变量</h2><p>典型错误：</p><pre><code class="lang-text">metrics: got variable `${SYMBOLICATOR_STATSD_ADDR}`, but it does not exist</code></pre><p>没有配置 StatsD 时，删除 Metrics 相关配置，使用最小 Symbolicator 配置即可。</p><hr><h1>十七、域名、CDN、HTTPS 与防火墙</h1><p>先在内网确认：</p><pre><code class="lang-text">http://10.0.10.3/</code></pre><p>正常后再接入域名。</p><p>建议顺序：</p><pre><code class="lang-text">1. 添加 DNS A 记录；
2. 先保持 DNS-only；
3. 验证 http://sentry.example.com/；
4. 修改 Sentry 的公开 URL；
5. 配置 HTTPS；
6. 最后启用 CDN。</code></pre><p>修改 <code>/etc/sentry/config.yml</code>：</p><pre><code class="lang-yaml">system.url-prefix: &#039;https://sentry.example.com&#039;
system.internal-url-prefix: &#039;http://127.0.0.1:9001&#039;</code></pre><p>应用配置：</p><pre><code class="lang-bash">cd /opt/self-hosted-26.7.2

docker compose \
  -f docker-compose.app.yml \
  restart web relay</code></pre><p>公网只应放行：</p><pre><code class="lang-text">80/tcp
443/tcp（启用 HTTPS 后）</code></pre><p>应保持内部访问或用防火墙限制的端口：</p><pre><code class="lang-text">3000
5432
6432
6379
8123
9000
9092
9901
11211
1218
3021
8333</code></pre><p>CDN 回源应连接 Envoy：</p><pre><code class="lang-text">http://源站:80</code></pre><p>不要回源到：</p><pre><code class="lang-text">:9001
:3000
:5432
:6432
:6379
:9092
:9000
:8123</code></pre><hr><h1>十八、备份重点</h1><p>至少纳入备份：</p><pre><code class="lang-text">PostgreSQL 数据库
ClickHouse 数据目录
Kafka 数据目录与 Topic 配置
SeaweedFS 数据目录
/etc/sentry/
/etc/envoy/
/etc/postgresql/
/etc/pgbouncer/
/etc/kafka/
/etc/clickhouse-server/
/etc/seaweedfs/</code></pre><p>PostgreSQL 逻辑备份示例：</p><pre><code class="lang-bash">mkdir -p /backup

sudo -u postgres \
  pg_dump \
  -Fc \
  sentry \
  &gt; /backup/sentry-$(date +%F).dump</code></pre><hr><h1>总结</h1><p>这套架构的核心不是“完全不用 Docker”，而是：</p><pre><code class="lang-text">PostgreSQL、Kafka、ClickHouse、Redis、SeaweedFS
    → 运行在宿主机

Sentry、Snuba、Relay、任务服务、Consumer
    → 运行在 Docker

Envoy
    → 运行在宿主机，作为唯一入口</code></pre><p>最终诊断顺序应固定为：</p><pre><code class="lang-text">1. PostgreSQL 是否 online
2. PgBouncer 6432 是否可连接
3. Redis 是否 PONG
4. Kafka 是否监听 9092
5. ClickHouse 是否可查询
6. SeaweedFS Bucket 是否存在
7. Sentry Web 的 /_health/ 是否 200
8. Sentry Web 的 / 是否 302
9. Envoy 的 /_health/ 是否 200
10. Envoy 的 / 是否 302
11. 浏览器通过 :80 是否进入登录页
12. SDK 是否可成功发送测试事件</code></pre><p>最终用户入口只有：</p><pre><code class="lang-text">http://服务器IP/</code></pre><p>或后续的：</p><pre><code class="lang-text">https://sentry.example.com/</code></pre><h1>Sentry 半裸机部署故障修复补充：从 Relay 403 到事件正常入库</h1><p>上一篇文章完成了 Debian 13 上的 Sentry 半裸机部署：</p><pre><code class="lang-text">PostgreSQL / PgBouncer
Redis
Memcached
Kafka
ClickHouse
SeaweedFS
Envoy</code></pre><p>其中基础设施运行在宿主机，Sentry、Relay、Snuba 等应用组件运行在 Docker，并统一使用：</p><pre><code class="lang-yaml">network_mode: host</code></pre><p>部署完成后，Sentry Web 可以正常打开，Relay 也可以正常启动，但是从 SDK 发送的错误始终没有出现在 Issues 页面。</p><p>这次补充记录从故障现象、源码定位到最终修复的完整过程。</p><hr><h2>一、最终故障链路</h2><p>最开始以为问题发生在 Relay：</p><pre><code class="lang-text">SDK
  ↓
Envoy :80
  ↓
Relay :3000
  ↓
Sentry</code></pre><p>实际上，完整链路是：</p><pre><code class="lang-text">SDK
  ↓
Envoy
  ↓
Relay
  ↓
Kafka ingest-events
  ↓
events-consumer
  ↓
taskworker
  ↓
SeaweedFS nodestore
  ↓
Kafka events
  ↓
Snuba errors consumer
  ↓
ClickHouse errors_local
  ↓
Sentry Issues 页面</code></pre><p>最终发现并不是一个问题，而是连续存在两个问题：</p><ol><li>Relay 没有被 Sentry 认定为内部 Relay，导致项目配置接口返回 <code>403</code>；</li><li>修复 Relay 后，事件进入消费队列，但 taskworker 写入 SeaweedFS 时因 S3 签名错误失败。</li></ol><p>只有两个问题都修复，事件才会最终显示在 Issues 页面。</p><hr><h1>二、第一阶段：Relay 获取项目配置返回 403</h1><h2>1. 初始日志</h2><p>Relay 日志持续出现：</p><pre><code class="lang-text">failed to fetch global config from upstream
upstream request returned error 403 Forbidden
can&#039;t fetch project states
Health check probe &#039;auth&#039; failed</code></pre><p>Sentry Web 日志对应显示：</p><pre><code class="lang-text">Forbidden: /api/0/relays/projectconfigs/
status_code=403</code></pre><p>具体请求是：</p><pre><code class="lang-text">POST /api/0/relays/projectconfigs/?version=3</code></pre><p>这说明：</p><pre><code class="lang-text">Relay 可以连接 Web
Web 也确实收到了请求
但 Web 拒绝了 Relay 的身份</code></pre><p>因此当时可以排除：</p><pre><code class="lang-text">Relay upstream 地址错误
Relay 容器无法连接 Web
Envoy 路由错误
Relay 凭据文件不存在
Relay 版本不匹配</code></pre><hr><h2>2. 检查 Relay 数据库记录</h2><p>通过 Sentry Django Shell 查询 Relay：</p><pre><code class="lang-python">from django.apps import apps

Relay = apps.get_model(&quot;sentry&quot;, &quot;Relay&quot;)

relay = Relay.objects.get(
    relay_id=&quot;Relay ID&quot;
)

print({
    &quot;relay_id&quot;: relay.relay_id,
    &quot;public_key&quot;: relay.public_key,
    &quot;first_seen&quot;: relay.first_seen,
    &quot;last_seen&quot;: relay.last_seen,
    &quot;is_internal&quot;: relay.is_internal,
})</code></pre><p>当时的结果是：</p><pre><code class="lang-text">relay_id: 匹配
public_key: 匹配
first_seen: None
last_seen: None
is_internal: False</code></pre><p>Relay 凭据文件中的：</p><pre><code class="lang-text">id
public_key</code></pre><p>都和数据库记录匹配，凭据文件本身也能被 Relay 读取。</p><p>但：</p><pre><code class="lang-text">first_seen = None
last_seen = None</code></pre><p>说明 Relay 从未成功完成被 Sentry 接受的注册流程。</p><hr><h1>三、从 Sentry 26.7.2 源码定位 403 原因</h1><p>最关键的源码位于：</p><pre><code class="lang-text">sentry/api/endpoints/relay/project_configs.py</code></pre><p><code>RelayProjectConfigsEndpoint</code> 的核心代码是：</p><pre><code class="lang-python">authentication_classes = (RelayAuthentication,)
permission_classes = (RelayPermission,)</code></pre><p>真正返回 403 的判断是：</p><pre><code class="lang-python">relay = request.relay

if not relay.is_internal:
    return Response(
        &quot;Relay unauthorized for config information&quot;,
        status=403,
    )</code></pre><p>这说明：</p><pre><code class="lang-text">projectconfigs 接口只允许 internal Relay 获取全局配置和项目配置</code></pre><p>接着读取 Relay 注册代码：</p><pre><code class="lang-text">sentry/api/endpoints/relay/register_response.py</code></pre><p>其中有：</p><pre><code class="lang-python">is_internal = is_internal_relay(request, public_key)</code></pre><p>并且每次注册都会重新写入：</p><pre><code class="lang-python">if relay.is_internal != is_internal:
    relay.is_internal = is_internal
    relay.save()</code></pre><p>因此手动修改：</p><pre><code class="lang-python">relay.is_internal = True
relay.save()</code></pre><p>不是可靠修复。</p><p>Relay 下次重新注册后，Sentry 会再次计算：</p><pre><code class="lang-text">is_internal_relay(...)</code></pre><p>如果判断结果仍然是 <code>False</code>，数据库就会被重新写回：</p><pre><code class="lang-text">is_internal=False</code></pre><hr><h2>1. Sentry 判断内部 Relay 的三种方式</h2><p>在：</p><pre><code class="lang-text">sentry/api/authentication.py</code></pre><p>中，Sentry 26.7.2 的判断逻辑是：</p><pre><code class="lang-python">def is_internal_relay(request, public_key):
    if settings.DEBUG:
        return True

    if public_key in settings.SENTRY_RELAY_WHITELIST_PK:
        return True

    if is_internal_ip(request) and options.get(&quot;relay.allow_internal_ip_auth&quot;):
        return True

    return False</code></pre><p>也就是说，Relay 必须满足以下任一条件：</p><h3>方式一：开发模式</h3><pre><code class="lang-python">settings.DEBUG is True</code></pre><p>生产环境不应该使用。</p><h3>方式二：公钥白名单</h3><pre><code class="lang-python">public_key in settings.SENTRY_RELAY_WHITELIST_PK</code></pre><h3>方式三：内部 IP 认证</h3><pre><code class="lang-python">is_internal_ip(request)
and options.get(&quot;relay.allow_internal_ip_auth&quot;)</code></pre><p>我们确认数据库选项：</p><pre><code class="lang-text">relay.allow_internal_ip_auth=True</code></pre><p>但由于当前是：</p><pre><code class="lang-text">network_mode: host</code></pre><p>Relay 的请求来源没有被 Sentry 的 <code>is_internal_ip()</code> 逻辑识别为允许的内部地址。</p><p>所以最终表现为：</p><pre><code class="lang-text">allow_internal_ip_auth=True
但 is_internal_relay() 仍然返回 False</code></pre><hr><h1>四、第一项修复：加入 Relay 公钥白名单</h1><p>当前部署只有一个本机 Relay，最稳定的做法是将该 Relay 的公钥加入 Sentry 白名单。</p><p>首先备份配置：</p><pre><code class="lang-bash">cp -a \
  /etc/sentry/sentry.conf.py \
  /etc/sentry/sentry.conf.py.bak.relay-whitelist.$(date +%F-%H%M%S)</code></pre><p>读取 Relay 公钥：</p><pre><code class="lang-bash">jq -r .public_key /etc/sentry/relay/credentials.json</code></pre><p>然后在：</p><pre><code class="lang-text">/etc/sentry/sentry.conf.py</code></pre><p>中加入：</p><pre><code class="lang-python">SENTRY_RELAY_WHITELIST_PK = [
    &quot;Relay 的 public_key&quot;
]</code></pre><p>实际操作时不要把公钥和 Secret 发到聊天记录中。</p><p>然后强制重建 Web 和 Relay：</p><pre><code class="lang-bash">cd /opt/self-hosted-26.7.2

docker compose \
  -f docker-compose.app.yml \
  up -d --force-recreate web relay</code></pre><hr><h2>1. 验证 Relay 认证恢复</h2><p>查询有效配置：</p><pre><code class="lang-bash">docker compose \
  -f docker-compose.app.yml \
  exec -T web \
  sentry shell -c &#039;
from django.conf import settings

print(len(settings.SENTRY_RELAY_WHITELIST_PK))
print(settings.SENTRY_RELAY_WHITELIST_PK[0][:12] + &quot;...&quot;)
&#039;</code></pre><p>查询 Relay 状态：</p><pre><code class="lang-bash">docker compose \
  -f docker-compose.app.yml \
  exec -T web \
  sentry shell -c &#039;
from django.apps import apps

Relay = apps.get_model(&quot;sentry&quot;, &quot;Relay&quot;)
RelayUsage = apps.get_model(&quot;sentry&quot;, &quot;RelayUsage&quot;)

relay = Relay.objects.get(
    relay_id=&quot;Relay ID&quot;
)

print({
    &quot;is_internal&quot;: relay.is_internal,
})

print(list(
    RelayUsage.objects.filter(
        relay_id=relay.relay_id
    ).values(
        &quot;version&quot;,
        &quot;first_seen&quot;,
        &quot;last_seen&quot;,
    )
))
&#039;</code></pre><p>修复后结果：</p><pre><code class="lang-text">is_internal: True
version: 26.7.2
first_seen: 有值
last_seen: 持续更新</code></pre><p>这说明 Relay 已经成功完成：</p><pre><code class="lang-text">register/challenge
register/response
projectconfigs</code></pre><p>此前的：</p><pre><code class="lang-text">403 Forbidden
failed to fetch global config
Health check probe &#039;auth&#039; failed</code></pre><p>不再是当前阻塞点。</p><hr><h1>五、第二阶段：Relay 已恢复，但 Issues 仍然没有事件</h1><p>修复 Relay 后，向正确的 Sentry Store API 发送事件：</p><pre><code class="lang-bash">curl -X POST \
  &quot;http://127.0.0.1/api/1/store/?sentry_key=项目公钥&quot; \
  -H &quot;Content-Type: application/json&quot; \
  --data-binary @event.json</code></pre><p>返回：</p><pre><code class="lang-http">HTTP/1.1 200 OK</code></pre><p>响应包含：</p><pre><code class="lang-json">{
  &quot;id&quot;: &quot;事件 ID&quot;
}</code></pre><p>Envoy 的 Relay 上游计数也增加：</p><pre><code class="lang-text">rq_success 增加
rq_total 增加
rq_error 没有增加
rq_timeout=0</code></pre><p>说明：</p><pre><code class="lang-text">Envoy → Relay → Kafka</code></pre><p>已经正常。</p><p>但是 ClickHouse 中：</p><pre><code class="lang-sql">SELECT count()
FROM errors_local;</code></pre><p>仍然是：</p><pre><code class="lang-text">0</code></pre><p>于是继续检查 Kafka 和消费者。</p><hr><h1>六、Kafka 消费链路定位</h1><p>检查 Consumer Group：</p><pre><code class="lang-bash">/opt/confluent-7.6.6/bin/kafka-consumer-groups \
  --bootstrap-server 127.0.0.1:9092 \
  --group ingest-consumer \
  --describe</code></pre><p>结果显示：</p><pre><code class="lang-text">ingest-events:
CURRENT-OFFSET = LOG-END-OFFSET
LAG = 0</code></pre><p>说明：</p><pre><code class="lang-text">events-consumer 正常消费 ingest-events</code></pre><p>但是继续检查 <code>events</code> Topic：</p><pre><code class="lang-bash">/opt/confluent-7.6.6/bin/kafka-get-offsets \
  --bootstrap-server 127.0.0.1:9092 \
  --topic events</code></pre><p>在问题尚未修复时：</p><pre><code class="lang-text">events:0:0</code></pre><p>于是判断：</p><pre><code class="lang-text">事件已经进入 ingest-events
events-consumer 也在工作
但保存事件的后续任务失败</code></pre><hr><h1>七、真正的第二个根因：SeaweedFS S3 签名错误</h1><p>查看 taskworker 日志：</p><pre><code class="lang-bash">docker logs \
  --since 30m \
  sentry-self-hosted-taskworker-1</code></pre><p>发现关键错误：</p><pre><code class="lang-text">botocore.exceptions.ClientError:
An error occurred (SignatureDoesNotMatch)
when calling the PutObject operation</code></pre><p>完整含义是：</p><pre><code class="lang-text">taskworker 无法把事件内容写入 nodestore</code></pre><p>Sentry 的错误事件不是只写 ClickHouse。事件原始内容还要写入 nodestore，后续任务需要从 nodestore 读取原始事件。</p><p>如果 nodestore 写入失败，就会导致：</p><pre><code class="lang-text">事件无法完整保存
Issue 无法正常创建</code></pre><hr><h2>1. 对比 Sentry 和 SeaweedFS 的凭据</h2><p>Sentry 的 nodestore 配置原来是：</p><pre><code class="lang-python">SENTRY_NODESTORE = &quot;sentry_nodestore_s3.S3PassthroughDjangoNodeStorage&quot;

SENTRY_NODESTORE_OPTIONS = {
    &quot;compression&quot;: True,
    &quot;endpoint_url&quot;: &quot;http://127.0.0.1:8333&quot;,
    &quot;bucket_path&quot;: &quot;nodestore&quot;,
    &quot;bucket_name&quot;: &quot;nodestore&quot;,
    &quot;region_name&quot;: &quot;us-east-1&quot;,
    &quot;aws_access_key_id&quot;: &quot;sentry&quot;,
    &quot;aws_secret_access_key&quot;: &quot;sentry&quot;,
}</code></pre><p>SeaweedFS systemd 服务实际使用的是：</p><pre><code class="lang-ini">Environment=AWS_ACCESS_KEY_ID=sentry
Environment=AWS_SECRET_ACCESS_KEY=SeaweedFS 实际 Secret</code></pre><p>因此产生了差异：</p><pre><code class="lang-text">访问密钥：
Sentry       = sentry
SeaweedFS    = sentry

Secret：
Sentry       = sentry
SeaweedFS    = 另一个 Secret</code></pre><p>结果就是：</p><pre><code class="lang-text">S3 SignatureDoesNotMatch</code></pre><hr><h1>八、第二项修复：同步 SeaweedFS 凭据</h1><p>先备份配置：</p><pre><code class="lang-bash">cp -a \
  /etc/sentry/sentry.conf.py \
  /etc/sentry/sentry.conf.py.bak.s3.$(date +%F-%H%M%S)

cp -a \
  /etc/sentry/config.yml \
  /etc/sentry/config.yml.bak.s3.$(date +%F-%H%M%S)</code></pre><p>读取 SeaweedFS 的实际 Secret：</p><pre><code class="lang-bash">systemctl show seaweedfs \
  --property=Environment \
  --value</code></pre><p>不要把完整 Secret 输出到聊天或日志中。</p><p>将该 Secret 同步到以下两处：</p><h2>1. <code>/etc/sentry/sentry.conf.py</code></h2><pre><code class="lang-python">SENTRY_NODESTORE_OPTIONS = {
    &quot;compression&quot;: True,
    &quot;endpoint_url&quot;: &quot;http://127.0.0.1:8333&quot;,
    &quot;bucket_path&quot;: &quot;nodestore&quot;,
    &quot;bucket_name&quot;: &quot;nodestore&quot;,
    &quot;region_name&quot;: &quot;us-east-1&quot;,
    &quot;aws_access_key_id&quot;: &quot;sentry&quot;,
    &quot;aws_secret_access_key&quot;: &quot;与SeaweedFS完全相同的Secret&quot;,
}</code></pre><h2>2. <code>/etc/sentry/config.yml</code></h2><pre><code class="lang-yaml">filestore.profiles-backend: &#039;s3&#039;
filestore.profiles-options:
  access_key: &quot;sentry&quot;
  secret_key: &quot;与SeaweedFS完全相同的Secret&quot;
  bucket_name: &quot;profiles&quot;
  region_name: &quot;us-east-1&quot;
  endpoint_url: &quot;http://127.0.0.1:8333&quot;
  addressing_style: &quot;path&quot;
  signature_version: &quot;s3v4&quot;</code></pre><hr><h2>3. 确认 bucket 存在</h2><pre><code class="lang-bash">export AWS_ACCESS_KEY_ID=sentry
export AWS_SECRET_ACCESS_KEY=&#039;SeaweedFS实际Secret&#039;
export AWS_DEFAULT_REGION=us-east-1

aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3api head-bucket \
  --bucket nodestore</code></pre><pre><code class="lang-bash">aws \
  --endpoint-url http://127.0.0.1:8333 \
  s3api head-bucket \
  --bucket profiles</code></pre><p>本次检查结果：</p><pre><code class="lang-text">nodestore exists
profiles exists</code></pre><hr><h2>4. 重建读取 S3 配置的服务</h2><p>配置修改后，重建：</p><pre><code class="lang-bash">cd /opt/self-hosted-26.7.2

docker compose \
  -f docker-compose.app.yml \
  up -d \
  --force-recreate \
  web \
  taskworker \
  taskscheduler \
  events-consumer \
  post-process-forwarder-errors</code></pre><p>不能只重启 Web，因为实际写 nodestore 的是：</p><pre><code class="lang-text">taskworker</code></pre><p>必须确保 taskworker 读取到新配置。</p><hr><h1>九、最终验证</h1><p>重新发送测试事件：</p><pre><code class="lang-bash">EVENT_ID=$(cat /proc/sys/kernel/random/uuid | tr -d -)

cat &gt;/tmp/sentry-test.json &lt;&lt;EOF
{
  &quot;event_id&quot;: &quot;$EVENT_ID&quot;,
  &quot;message&quot;: &quot;S3 signature repair verification&quot;,
  &quot;level&quot;: &quot;error&quot;,
  &quot;platform&quot;: &quot;python&quot;
}
EOF

curl -X POST \
  &quot;http://127.0.0.1/api/1/store/?sentry_key=项目公钥&quot; \
  -H &quot;Content-Type: application/json&quot; \
  --data-binary @/tmp/sentry-test.json</code></pre><p>响应：</p><pre><code class="lang-json">{
  &quot;id&quot;: &quot;e764aa428bd24086af8ee733acd51294&quot;
}</code></pre><p>然后查询 ClickHouse：</p><pre><code class="lang-bash">clickhouse-client \
  --query &quot;
    SELECT
      event_id,
      project_id,
      timestamp,
      message
    FROM default.errors_local
    WHERE event_id = &#039;e764aa428bd24086af8ee733acd51294&#039;
    FORMAT TSVWithNames
  &quot;</code></pre><p>最终结果：</p><pre><code class="lang-text">event_id                                  project_id  timestamp            message
e764aa42-8bd2-4086-af8e-e733acd51294      1           2026-08-17 17:14:57  S3 signature repair verification</code></pre><p>这证明事件已经完成：</p><pre><code class="lang-text">HTTP 接收
→ Relay
→ Kafka
→ events-consumer
→ taskworker
→ SeaweedFS nodestore
→ Snuba
→ ClickHouse</code></pre><p>同时，之前的错误：</p><pre><code class="lang-text">SignatureDoesNotMatch</code></pre><p>已不再出现。</p><hr><h1>十、最终状态</h1><p>当前关键链路状态如下：</p><table><thead><tr><th>组件</th><th>状态</th></tr></thead><tbody><tr><td>Envoy</td><td>正常转发</td></tr><tr><td>Relay</td><td>已成功认证</td></tr><tr><td>Relay <code>is_internal</code></td><td><code>True</code></td></tr><tr><td>Relay <code>last_seen</code></td><td>持续更新</td></tr><tr><td>Relay → Kafka</td><td>正常</td></tr><tr><td><code>events-consumer</code></td><td>正常消费</td></tr><tr><td><code>taskworker</code></td><td>已能写入 SeaweedFS</td></tr><tr><td>SeaweedFS <code>nodestore</code></td><td>正常</td></tr><tr><td>SeaweedFS <code>profiles</code></td><td>正常</td></tr><tr><td>Snuba errors consumer</td><td>正常消费</td></tr><tr><td>ClickHouse <code>errors_local</code></td><td>已写入事件</td></tr><tr><td>Sentry Issues</td><td>新事件可正常生成</td></tr></tbody></table><hr><h1>十一、仍然存在但不阻塞事件的日志</h1><h2>1. GeoIP 数据库缺失</h2><p>日志：</p><pre><code class="lang-text">Error opening GeoIP database:
/geoip/GeoLite2-City.mmdb</code></pre><p>这只影响：</p><pre><code class="lang-text">IP 地理位置
城市
国家
地区</code></pre><p>不影响错误事件接收、存储和 Issues 创建。</p><p>如果以后需要 GeoIP，可以下载：</p><pre><code class="lang-text">GeoLite2-City.mmdb</code></pre><p>放到：</p><pre><code class="lang-text">/etc/sentry/geoip/GeoLite2-City.mmdb</code></pre><p>并确认容器挂载：</p><pre><code class="lang-yaml">- /etc/sentry/geoip:/geoip:ro</code></pre><p>然后重建相关服务。</p><hr><h2>2. Redis 锁竞争警告</h2><p>日志：</p><pre><code class="lang-text">UnableToAcquireLock:
Could not set key</code></pre><p>例如：</p><pre><code class="lang-text">detect_escalation</code></pre><p>这是多个任务同时竞争同一个 Redis 锁产生的警告，不影响基础错误事件写入。</p><p>如果频繁出现，需要另行检查：</p><pre><code class="lang-text">Redis 可用性
Redis 持久化
任务并发量
taskworker 数量</code></pre><p>但它不是本次事件不显示的根因。</p><hr><h1>十二、这次故障的经验总结</h1><h2>1. HTTP 200 不代表 Issues 一定会出现</h2><p>Store API 返回：</p><pre><code class="lang-text">200 OK</code></pre><p>只能说明事件被入口接受，不能证明事件已经成功写入：</p><pre><code class="lang-text">nodestore
ClickHouse
Sentry Issue</code></pre><p>必须继续检查：</p><pre><code class="lang-text">Kafka offset
Consumer lag
taskworker 日志
ClickHouse 数据</code></pre><hr><h2>2. Relay 认证和事件入库是两个独立问题</h2><p>第一阶段的问题：</p><pre><code class="lang-text">Relay projectconfigs 403</code></pre><p>第二阶段的问题：</p><pre><code class="lang-text">SeaweedFS SignatureDoesNotMatch</code></pre><p>两者表现都可能是：</p><pre><code class="lang-text">SDK 发送后，Issues 没有数据</code></pre><p>但修复点完全不同。</p><hr><h2>3. S3 兼容服务的三项配置必须完全一致</h2><p>Sentry 使用 SeaweedFS 时，至少要保证以下配置一致：</p><pre><code class="lang-text">endpoint_url
access key
secret key
region
signature version
addressing style</code></pre><p>本次核心差异是：</p><pre><code class="lang-text">SeaweedFS Secret ≠ Sentry Secret</code></pre><p>即使：</p><pre><code class="lang-text">endpoint 正确
bucket 存在
access key 正确</code></pre><p>只要 Secret 不同，仍然会出现：</p><pre><code class="lang-text">SignatureDoesNotMatch</code></pre><hr><h2>4. <code>network_mode: host</code> 会影响 Relay 内部身份判断</h2><p>Host Network 的优点是：</p><pre><code class="lang-text">应用层可以直接访问宿主机服务
配置简单
避免 Docker DNS 与宿主机服务混用</code></pre><p>但它也会改变：</p><pre><code class="lang-text">请求来源 IP
容器网络边界
Sentry 对 Relay 来源的内部 IP 判断</code></pre><p>因此在半裸机部署中，建议为本机 Relay 配置明确的：</p><pre><code class="lang-python">SENTRY_RELAY_WHITELIST_PK</code></pre><p>不要完全依赖：</p><pre><code class="lang-python">relay.allow.internal.ip.auth</code></pre><p>因为实际来源 IP 是否被 Sentry 识别为 internal，还取决于：</p><pre><code class="lang-text">Host Network
代理层
监听地址
请求路径
Sentry 内部 IP 判断规则</code></pre><hr><h1>十三、最终排查顺序建议</h1><p>以后遇到“SDK 发送成功但 Issues 没有事件”，建议严格按照以下顺序检查：</p><pre><code class="lang-text">1. Store API HTTP 状态码
2. Envoy → Relay 请求计数
3. Relay 是否存在 403 / auth 错误
4. Kafka ingest-events 是否增长
5. ingest-consumer 是否有 lag
6. taskworker 是否出现异常
7. events Topic 是否增长
8. snuba-consumers 是否有 lag
9. ClickHouse errors_local 是否有数据
10. Web Issues API 是否能查询到事件</code></pre><p>对应命令：</p><pre><code class="lang-bash">docker logs --since 10m sentry-self-hosted-relay-1</code></pre><pre><code class="lang-bash">/opt/confluent-7.6.6/bin/kafka-consumer-groups \
  --bootstrap-server 127.0.0.1:9092 \
  --group ingest-consumer \
  --describe</code></pre><pre><code class="lang-bash">docker logs --since 10m sentry-self-hosted-taskworker-1</code></pre><pre><code class="lang-bash">/opt/confluent-7.6.6/bin/kafka-consumer-groups \
  --bootstrap-server 127.0.0.1:9092 \
  --group snuba-consumers \
  --describe</code></pre><pre><code class="lang-bash">clickhouse-client \
  --query &quot;SELECT count() FROM default.errors_local&quot;</code></pre><p>这次最终故障不是单纯的 Relay 问题，而是：</p><pre><code class="lang-text">Relay 内部认证未配置
+
SeaweedFS S3 Secret 与 Sentry 不一致</code></pre><p>两个问题修复后，Sentry 已能够从入口接收事件、经过 Kafka 和消费者处理，并最终在 ClickHouse 中查询到错误事件。</p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/69.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>记录：virtualizor开的win服务器，不正常初始化</title><link>https://www.imxb.de/new_archives/68.shtml</link><guid>https://www.imxb.de/new_archives/68.shtml</guid><pubDate>Tue, 28 Jul 2026 00:58:39 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[本文环境是virtualizor+pve，去年的经历，在这里做个备忘。阿三的virtualizor，默认情况开通windows可以说是啥都不做：ip不配置、甚至远程桌面都不开。解决如下：在vz后...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>本文环境是virtualizor+pve，去年的经历，在这里做个备忘。<br>阿三的virtualizor，默认情况开通windows可以说是啥都不做：ip不配置、甚至远程桌面都不开。<br>解决如下：<br>在vz后台 主机配置（这个翻译有点诡异.. master settings）里 找到 安全 -- 使用windows配置脚本，可以自动配置ip和密码<br>利用recipe在开机的时候自动运行如下命令，开启远程桌面和关闭防火墙</p><p>wmic RDTOGGLE WHERE ServerName='%COMPUTERNAME%' call SetAllowTSConnections 1<br>netsh advfirewall set allprofiles state off<br>netsh firewall set opmode mode=disable</p><p>接下来重装测试，可以直接连接进入vps了，不需要上vnc做配置。</p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/68.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>【排版分享】2025年普通高等学校招生统一考试 语文 全国一卷</title><link>https://www.imxb.de/new_archives/66.shtml</link><guid>https://www.imxb.de/new_archives/66.shtml</guid><pubDate>Sat, 18 Jul 2026 22:12:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[使用网传试题自行排版，非原卷。2025年普通高等学校招生统一考试 语文 全国一卷排版软件：方正书版9.11 + 方正 PDFGenerator浙江省风格卷头。使用了图片转文字，文字可能有部分差错...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p><strong>使用网传试题自行排版，非原卷。</strong></p><p>2025年普通高等学校招生统一考试 语文 全国一卷</p><p>排版软件：方正书版9.11 + 方正 PDFGenerator</p><p>浙江省风格卷头。使用了图片转文字，文字可能有部分差错没校对出来。（页脚占两格的数字我暂时解决不了</p><p>暂不提供源文件。</p><p>下载地址：<a href="https://data.imxb.de/upload2026/files/doc/2026/07/18/3092959e-4db6-d315-a8eb-d7d234bce508.pdf"><a href="https://data.imxb.de/upload2026/files/doc/2026/07/18/3092959e-4db6-d315-a8eb-d7d234bce508.pdf">https://data.imxb.de/upload2026/files/doc/2026/07/18/3092959e-4db6-d315-a8eb-d7d234bce508.pdf</a></a></p>
]]></content:encoded><slash:comments>1</slash:comments><comments>https://www.imxb.de/new_archives/66.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>随手记：Win2008/WHS2011 开启TLS1.2支持</title><link>https://www.imxb.de/new_archives/65.shtml</link><guid>https://www.imxb.de/new_archives/65.shtml</guid><pubDate>Fri, 19 Jun 2026 23:49:46 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[在使用windows home server 2011的时候发现，chrome访问web远程访问页面提示不安全的安全套件（IIS支持的TLS版本过低）可以通过修改注册表的方式，启用TLS1.2支...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>在使用windows home server 2011的时候发现，chrome访问web远程访问页面提示不安全的安全套件（IIS支持的TLS版本过低）</p><p>可以通过修改注册表的方式，启用TLS1.2支持 让现代浏览器正常访问。<br>新建一个txt文件，写入以下内容后重命名后缀为reg，双击合并，重启电脑即可。</p><pre><code>Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2]
&quot;DisabledByDefault&quot;=dword:00000000
&quot;Enabled&quot;=dword:00000001

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
&quot;DisabledByDefault&quot;=dword:00000000
&quot;Enabled&quot;=dword:00000001

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server]
&quot;DisabledByDefault&quot;=dword:00000000
&quot;Enabled&quot;=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
&quot;SystemDefaultTlsVersions&quot;=dword:00000001
&quot;SchUseStrongCrypto&quot;=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]
&quot;SystemDefaultTlsVersions&quot;=dword:00000001
&quot;SchUseStrongCrypto&quot;=dword:00000001</code></pre><p>记得trustasia以前有个工具专门配置这玩意的，找不到了（懒得找），直接改注册表得了。</p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/65.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>数据备份，该怎么做？我又是怎么落实的？</title><link>https://www.imxb.de/new_archives/64.shtml</link><guid>https://www.imxb.de/new_archives/64.shtml</guid><pubDate>Sun, 07 Jun 2026 00:00:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[&ensp;&ensp;&ensp;&ensp;在数字化办公日益普及的今天，数据已成为个人与企业最核心的资产。合同、方案等重要资料一旦丢失，或遭遇勒索病毒攻击，往往无法挽回。近期，网上频频曝出“...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>&ensp;&ensp;&ensp;&ensp;在数字化办公日益普及的今天，数据已成为个人与企业最核心的资产。合同、方案等重要资料一旦丢失，或遭遇勒索病毒攻击，往往无法挽回。近期，网上频频曝出“XX商家跑路”的消息，让数据安全这一老生常谈的话题再次被推到聚光灯下——你辛苦积累的资料，可能因为一个平台的倒下而瞬间化为乌有。</p><p>&ensp;&ensp;&ensp;&ensp;然而令人遗憾的是，绝大多数人依然没有真正建立起“备份”的习惯。究其原因，并非技术门槛高，而是“没经历过”——没有亲身体会过数据丢失后那种四处求索、试图挽回却无能为力的痛苦。正因为痛感缺失，侥幸心理才始终占据上风。</p><p>&ensp;&ensp;&ensp;&ensp;说实话，很长一段时间里，我也抱着侥幸心理，几乎没做过备份——哪怕我自己已经经历过好几次、各种类型的数据丢失。勒索病毒、硬盘损坏、提供商跑路等等...我都经历过了。但随着业务规模不断扩大，服务器上的数据对我而言越来越重要，我不得不开始做出一些改变了。</p><p>&ensp;&ensp;&ensp;&ensp;那么，该怎么备份，才足够安全，也足够简单呢？来了解一下<del>老生常谈的</del><strong>3-2-1备份原则</strong>。说实话，他很简单，简单到可能没办法凑成一篇文章。但是，他却是无数人用宝贵数据丢失的惨痛教训验证过的、业界公认可靠的方案。大致规则如下：</p><ul><li><strong>3</strong> - 3份副本。一份原件，两份副本。总之，就是要有多份备份。</li><li><strong>2</strong> - 2种介质。别把所有备份放在同一类设备上。比如“本地硬盘+云盘”，或者“本地硬盘+u盘”，都是不错的选择。u盘虽然方便，但绝不能是唯一的备份，因为它太容易失踪了。不同介质可以避免共性故障（比如两块盘同时暴毙的情况，总有的）。总之，<strong>鸡蛋不能放在一个篮子里</strong>。</li><li><strong>1</strong> - 1份异地。必须有一份备份存在和数据所在地隔离的位置，避免火灾、盗窃等物理灾难，防止意外发生。</li></ul><p><img src="https://data.imxb.de/pic/i/2026/06/06/F4A55DA3-7214-462E-8416-E059D05EB1F0.png" alt="image" title="image"></p><p>&ensp;&ensp;&ensp;&ensp;当然，数字都是最低要求。多留几份，可能麻烦点，但是肯定没坏处。多存几份、换着花样存、分开地方存，准没错。</p><p>&ensp;&ensp;&ensp;&ensp;我是怎么落实的？</p><p>&ensp;&ensp;&ensp;&ensp;首先，备份通过Nodejs开发的程序产生，每日凌晨（业务低峰）全量备份成一个zip压缩包（推荐是增量备份，但是我的数据量不算大，产物2G目前还没到），保留12份，经过验证其内容物可以完整恢复整个业务。</p><ul><li>3 - 一份原件、一份云盘、一份第三方服务器、一份本地硬盘、一份U盘。前几个可以由程序自动完成，不过呢，本地的硬盘和u盘显然不能，只能我手动下载保存。因此，在实际流程中，能保持最新的备份只有两到三份（源服务器回国网络质量问题），另外两份是有偏差的，只能作为兜底。这也是为什么我不觉得我这个实践是完美的。</li><li>2 - 同上，出现了多个介质，并且u盘可以随身携带。</li><li>1 - 原件所在地是香港，人在浙江，U盘随身携带、本地硬盘一直在身边，还有云盘（Google Drive）本身也会保障数据安全。云盘数据丢失的风险在于账号安全或者被平台封禁。</li></ul><p>&ensp;&ensp;&ensp;&ensp;说实话，可能不是最佳的实践，但是对我来说，绝对够用——因为它已经实实在在救过我一次了。</p><p>&ensp;&ensp;&ensp;&ensp;不做备份，无非就是嫌麻烦、寻思着“应该不会出事”呗。但残酷的现实会告诉你：勒索病毒、硬盘损坏、平台跑路——他们来之前可不会打招呼。表面的稳定运行，不过是暴风雨前的宁静罢了。</p><p>&ensp;&ensp;&ensp;&ensp;这是我在查资料时看到的一句话：<em>备份只有在您需要时可靠地进行还原才有意义</em>。记得偶尔试一试备份文件的可靠性，能不能恢复出完整的业务。万一备份出来的数据是损坏的，那前面的一切都是徒劳。</p><p>&ensp;&ensp;&ensp;&ensp;快去看看你的备份吧。</p><hr><p>&ensp;&ensp;&ensp;&ensp;顺便提一嘴：基于 3-2-1 原则，还有各种变体，本文暂且不提。如参考1中的“4-3-2-1”，成本太高，没必要。普通人只要做到“3-2-1”，就足够了。</p><hr><p>说明：本文部分文段（集中在上半篇）经过 deepseek 调整/润色。<br>参考：</p><ul><li><a href="https://zhuanlan.zhihu.com/p/551663850">https://zhuanlan.zhihu.com/p/551663850</a></li><li><a href="https://www.veeam.com/blog/cn/321-backup-rule.html">https://www.veeam.com/blog/cn/321-backup-rule.html</a></li></ul>
]]></content:encoded><slash:comments>1</slash:comments><comments>https://www.imxb.de/new_archives/64.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>一些奇奇怪怪的需求？word仿方正书版的几个小操作</title><link>https://www.imxb.de/new_archives/63.shtml</link><guid>https://www.imxb.de/new_archives/63.shtml</guid><pubDate>Sat, 30 May 2026 21:19:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[注意：以下仅为个人兴趣，实际排版工作中请使用更专业的排版软件，下面的操作可能是不规范的！1. 字体这里我只准备了最常用的方正书宋（宋体）和小标宋（标题用），安装替换即可。可能需要稍微调整行距。方...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>注意：以下仅为个人兴趣，实际排版工作中请使用更专业的排版软件，下面的操作可能是不规范的！</p><h1>1. 字体</h1><p>这里我只准备了最常用的方正书宋（宋体）和小标宋（标题用），安装替换即可。可能需要稍微调整行距。<br>方正书宋：<a href="https://soft.imxb.de/Others/%E6%96%B9%E6%AD%A3%E4%B9%A6%E5%AE%8B%E7%AE%80%E4%BD%93.zip">https://soft.imxb.de/Others/%E6%96%B9%E6%AD%A3%E4%B9%A6%E5%AE%8B%E7%AE%80%E4%BD%93.zip</a><br>方正小标宋：<a href="https://soft.imxb.de/Others/%E6%96%B9%E6%AD%A3%E5%B0%8F%E6%A0%87%E5%AE%8B%E7%AE%80.zip">https://soft.imxb.de/Others/%E6%96%B9%E6%AD%A3%E5%B0%8F%E6%A0%87%E5%AE%8B%E7%AE%80.zip</a><br>商用请注意版权，书宋是免费商用的，但是小标宋我记得不是。这里提供的书宋自带 NPS 风格英文字体，整个文档可以统一使用一个书宋。</p><h1>2. 标点</h1><p>方正书版默认使用开明式标点。</p><blockquote>开明式标点：凡表示一句结束的符号（如句号、问号、叹号、冒号等）用全角外，其它标点符号全部用对开（注：就是只占半格）</blockquote><p>word不支持开明式标点，截至发文，只能是通过修改每个标点的样式来做到。网上有很多不同的解决方案，个人试下来还是挤压标点的方式最管用，最近似。网上还有对标点设置缩放50%的方法，会导致标点变扁，不推荐使用。我这边文章是五号字，手动调整了一下发现挤压2.6pt的表现最好。</p><p>调试参数的方式：选中标点右键，Font-&gt;Advanced-&gt;Spacing，选择Condensed，然后在旁边设置距离。</p><p><img src="https://data.imxb.de/upload2026/files/doc/2026/05/30/379f3bb8-264d-7a13-62c8-63c33702bdf2.png" alt="img" title="img"></p><p>然后让ai写了个简单的宏来批量替换，就是每次编辑之后还得跑一次，新更新的内容并不能自动应用半格的标点样式。<br>根据文档的字体大小，需要手动调整压缩量，下面这一段VBA适用于五号大小的方正书宋。在word窗口按下alt+f11 粘贴后按F5运行即可批量应用样式。</p><pre><code class="lang-vb">Sub OpenStylePunctuationCondensed()
    &#039; 按开明制压缩以下标点（仅压缩标点本身，不变形）
    &#039; 压缩量：2.6 pt（与你调试的逗号一致）
    
    Dim targetChars As Variant
    &#039; 需要压缩的标点（中文开明制标准）
    targetChars = Array(&quot;，&quot;, &quot;、&quot;)
    
    Dim condenseAmount As Single
    condenseAmount = 2.6   &#039; 单位：磅
    
    Dim rng As Range
    Dim i As Integer
    Dim countPunct As Long
    countPunct = 0
    
    Application.ScreenUpdating = False
    
    For i = LBound(targetChars) To UBound(targetChars)
        Set rng = ActiveDocument.Content
        With rng.Find
            .Text = targetChars(i)
            .MatchWholeWord = False
            .Wrap = wdFindStop
            Do While .Execute
                &#039; 对标点本身应用紧缩
                rng.Font.Spacing = -condenseAmount
                countPunct = countPunct + 1
                rng.Collapse Direction:=wdCollapseEnd
            Loop
        End With
    Next i
    
    Application.ScreenUpdating = True
    
    MsgBox &quot;处理完成！&quot; &amp; vbCrLf &amp; _
           &quot;共处理 &quot; &amp; countPunct &amp; &quot; 个标点。&quot; &amp; vbCrLf &amp; _
           &quot;压缩量: &quot; &amp; condenseAmount &amp; &quot; pt&quot; &amp; vbCrLf &amp; _
           &quot;处理的标点: ， 、&quot;
End Sub</code></pre><h1>3. PDF 产物</h1><p>word导出的pdf自带microsoft word标记，我们修改PDF元数据，使其具有书版特征。<br>书版具有以下特征：</p><ul><li>PDF 1.4 （Arcobat 5.x）</li><li>元数据不包含书版信息，下方会说明具体值。</li></ul><p>使用 <a href="https://www.lddgo.net/file/modify-pdf-metadata">https://www.lddgo.net/file/modify-pdf-metadata</a> 先将PDF元数据修改成下图，这是书版导出的PDF格式。</p><p><img src="https://data.imxb.de/pic/i/2026/06/08/5282F7A7-B8BC-4BE5-B63B-354A6CF12399.png" alt="image" title="image"></p><p>Microsoft Office导出的PDF版本为1.7，需要使用 <a href="https://pdf.wdku.net/pdf_version_export">https://pdf.wdku.net/pdf_version_export</a> 降低到PDF1.4。接下来 PDF产物的模仿大致就结束了。修改日期这个没删掉，不管了。对比图如下，以书版说明书为例(左)，右为word仿品。</p><table><tr><td width=50%><center><img src="https://data.imxb.de/pic/i/2026/06/08/5054FC4E-8E98-468B-853D-3DA08A91E74C.png" alt="image" /></center></td><td><Center><img src="https://data.imxb.de/upload2026/files/doc/2026/06/08/ebae60e9-8d44-9c64-9632-baedeaa17e67.png" /></center></td></tr></table><p>其他的不是很了解，没有做模仿，待补充。</p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/63.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>随手记：windows右键菜单总是出现在鼠标左侧</title><link>https://www.imxb.de/new_archives/62.shtml</link><guid>https://www.imxb.de/new_archives/62.shtml</guid><pubDate>Fri, 24 Apr 2026 20:47:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[今天在更新服务器系统后，操作时，发现了一个很别扭的情况：在我按下右键时，菜单总是出现在鼠标的左侧。（正常情况应该出现在右侧）说实话，用了这么长时间的windows，第一次遇到这么诡异的情况。没有...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>今天在更新服务器系统后，操作时，发现了一个很别扭的情况：在我按下右键时，菜单总是出现在鼠标的左侧。（正常情况应该出现在右侧）</p><p>说实话，用了这么长时间的windows，第一次遇到这么诡异的情况。没有调整任何设置，就这么自己给自己改了。。。巨硬bug+1.</p><p>上网搜索答案后放在这里，以备不时之需。</p><p>WIN+R，<code>shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}</code>，进入平板电脑设置——其他——惯用左手，保存，恢复。</p><p><img src="https://data.imxb.de/pic/i/2026/04/24/xuefso.png" alt="image" title="image"></p><p><del>又水了一篇，证明我还在</del></p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/62.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>【图册】温州·永嘉·百丈瀑 2026.03.30</title><link>https://www.imxb.de/new_archives/61.shtml</link><guid>https://www.imxb.de/new_archives/61.shtml</guid><pubDate>Fri, 03 Apr 2026 22:22:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[大图警告，流量慎入！ 这里会有一些没有选入朋友圈的图片，属于是拍了、没有拍到人、没有涉及隐私的，都在这里了。就剩下十张，几乎没拍照属于是。爬山太累了，又不擅长拍照片。全是烂片，各位见笑，随便挂上...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p><strong>大图警告，流量慎入！</strong> 这里会有一些没有选入朋友圈的图片，属于是拍了、没有拍到人、没有涉及隐私的，都在这里了。就剩下十张，几乎没拍照属于是。</p><p>爬山太累了，又不擅长拍照片。<strong>全是烂片</strong>，各位见笑，随便挂上来凑凑数。</p><p><del>来的不是时候，瀑布跟个水龙头似的</del></p><p><img src="https://data.imxb.de/pic/i/2026/04/03/10qjdq2.jpg" alt="ed7304d461e5874096760eac33309d85" title="ed7304d461e5874096760eac33309d85"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10qkthz.jpg" alt="f64358cb07caeaf1451d60974c0b8d42" title="f64358cb07caeaf1451d60974c0b8d42"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10qfzo9.jpg" alt="b71f00fae583f98513da1563a4667531" title="b71f00fae583f98513da1563a4667531"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10qeb4e.jpg" alt="985767d5e2f20156a1338c88cd9d65d1" title="985767d5e2f20156a1338c88cd9d65d1"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10q0fdc.jpg" alt="85189afe01e5a460c4082de61e97f650" title="85189afe01e5a460c4082de61e97f650"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10pxqn4.jpg" alt="889d6b12883414e733bc9e1129d54604" title="889d6b12883414e733bc9e1129d54604"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10pszjq.jpg" alt="8de44b3a74d5575c02c73960b8fecc05" title="8de44b3a74d5575c02c73960b8fecc05"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10phfoo.jpg" alt="8d25aeaeb3ce1a70d6d1610128b32477" title="8d25aeaeb3ce1a70d6d1610128b32477"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10pgn8d.jpg" alt="8d20b9159480a387f0aa6e1b57295557" title="8d20b9159480a387f0aa6e1b57295557"><br><img src="https://data.imxb.de/pic/i/2026/04/03/10pfb5c.jpg" alt="1ec5e00b97a1f82c1ae8fd90937b2ca0" title="1ec5e00b97a1f82c1ae8fd90937b2ca0"></p>
]]></content:encoded><slash:comments>2</slash:comments><comments>https://www.imxb.de/new_archives/61.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>随手记：VMware下的Win7无法安装tools驱动</title><link>https://www.imxb.de/new_archives/60.shtml</link><guid>https://www.imxb.de/new_archives/60.shtml</guid><pubDate>Sat, 14 Mar 2026 21:27:39 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[环境：VMware ESXi 8.0U3 (by Broadcom)，Windows 7 x64 企业版 SP1.安装过程中，出现大量无法验证数字签名的错误，以及下面两个致命的错误：（此时安装过...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<p>环境：VMware ESXi 8.0U3 (by Broadcom)，Windows 7 x64 企业版 SP1.</p><p>安装过程中，出现大量无法验证数字签名的错误，以及下面两个致命的错误：</p><p><img src="https://data.imxb.de/pic/i/2026/03/14/yumdgb.png" alt="image" title="image"></p><p>（此时安装过程还能勉强继续，直到下面一个错误，安装程序开始回滚。）</p><p><img src="https://data.imxb.de/pic/i/2026/03/14/yv3ajd.png" alt="image" title="image"></p><p>修改兼容性为 Workstation 12，无效。上网查阅资料后，说是因为 Windows 7 不支持 SHA-2 签名，可通过打补丁解决。尝试安装 2019 年发布的KB4474419和KB4490628。</p><p>这里我提供一个ISO的直链，方便挂载安装：<a href="http://data.imxb.de/blog_down/vmtool_win7_sha2.iso"><a href="http://data.imxb.de/blog_down/vmtool_win7_sha2.iso">http://data.imxb.de/blog_down/vmtool_win7_sha2.iso</a></a> （来源：<a href="https://ihacksoft.com/archive/1397.html">https://ihacksoft.com/archive/1397.html</a>）</p><p>打完更新后，畅通无阻，安装过程顺利结束，没有任何报错。Aero 同样也正常工作。<br><img src="https://data.imxb.de/pic/i/2026/03/14/z6jsfc.png" alt="image" title="image"></p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/60.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item><item><title>在朝鲜 RedStar OS 3.0 上搭建网站？</title><link>https://www.imxb.de/new_archives/59.shtml</link><guid>https://www.imxb.de/new_archives/59.shtml</guid><pubDate>Thu, 19 Feb 2026 19:20:00 +0800</pubDate><dc:creator>xiobb</dc:creator><description><![CDATA[带过背景注意到朝鲜近几年的网站已经开始使用 next.js 开发了，不过版本嘛，一言难尽。。。Next.js 9.3.5搭配PHP5.6.发到群里，群友调侃性的建议我在红星OS上建站，于是就发生...]]></description><content:encoded xml:lang="zh-CN"><![CDATA[
<h1>带过背景</h1><p>注意到朝鲜近几年的网站已经开始使用 next.js 开发了，不过版本嘛，一言难尽。。。Next.js 9.3.5搭配PHP5.6.</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/r73w74.png" alt="f59edc96afec9264afd0318edfb26b80" title="f59edc96afec9264afd0318edfb26b80"></p><p>发到群里，群友调侃性的建议我在红星OS上建站，于是就发生了下面的一系列故事。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/r7ngiw.png" alt="image" title="image"></p><p>不过呢，RedStar 4.0 并没有泄露，因此下文只能围绕 RedStar 3.0 展开。</p><p>接下来开始折腾过程，边折腾边写，可能有些流水账，见谅。</p><h1>寻找镜像</h1><p>在 Archive.org 上搜索关键词 RedStar OS，发现红星OS 居然提供了Server版本，不过分为两个镜像，也许是有一个引导盘。</p><p>外链：<a href="https://archive.org/details/RedStarOS">https://archive.org/details/RedStarOS</a></p><p><img src="https://data.imxb.de/pic/i/2026/02/19/r9cpun.png" alt="image" title="image"></p><h1>安装</h1><h2>这就失败了？</h2><p>起初我尝试直接在服务器上安装。不过，HostHatch 同时只能挂载一个 ISO 镜像，并且更换需要重启服务器。因此，我接下来选择 在本地使用VirtualBox制作DD包，然后安装到服务器上面。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/s8xdy1.png" alt="c299aee5a8e4b321c664aebf796592a9" title="c299aee5a8e4b321c664aebf796592a9"></p><p>👆似乎是缺失驱动，并且Server版红星OS需要两个ISO，一个启动盘，一个应该是存数据的。</p><h2>尝试制作DD包（失败）</h2><p>尝试使用DD包的方式安装到服务器上。</p><p>创建一个VBOX虚拟机（因为他在Windows环境下可以模拟出类似KVM的环境：各种virtio设备，方便验证Virtio驱动是否有效）。红星3.0是基于RHEL开发的32位系统，这边我们选择Fedora 32bit，分配2核心和768M内存，10G磁盘。磁盘格式选择 VHD。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/sc73hm.png" alt="image" title="image"></p><p>先不选择iso镜像，一是我还在下载，二是接下来我需要把2个iso都挂进去。</p><p>编辑虚拟机，网络那一栏选择virtio-net。存储设备最终配置结果如下：</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/sf4pye.png" alt="image" title="image"></p><p>然后发现 我设置了Virtio，他还是能直接进入激活码页面😓 也就是说 上面应该是要同时挂载两个iso文件才可以安装。那不管了，先装完去dd试试看。</p><p>激活码，填满1，没有问题，能够继续。镜像这一块目测已经被破解了。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/si3aa6.png" alt="image" title="image"></p><p>好吧，打脸了。还是出现了没有找到磁盘的错误，没招。只能在本地安装，后面找个服务器开Virtualbox跑了。</p><h2>本地安装</h2><p>重新调整虚拟机配置，网卡 =&gt; <code>Intel Pro/1000 T 服务器 (82543GC)</code>；存储 =&gt; <code>PIIX4 (IDE)</code>。启动虚拟机。</p><p>提一嘴。红星OS3的激活码是5组，一组4位，共20位。不会自动跳到下一组，要按下Tab键。</p><p>下一步是密码设置，居然有密码强度检测，需要输入一个复杂一点的密码。因为这台机器要联网，不确定他会不会上传数据，我选择 <code>Aa.114514</code> 作为密码。（反正不会暴露 SSH，直接写在这里了）。</p><p>Desktop 版的红星OS会在所有经过他的文件末尾增加数据，可能是用于追踪。因此对于 Server 系统，还是留个心眼比较好。</p><p>下一步，关于磁盘分区。选择第一项“使用全部驱动器”。下面提供翻译后的截图。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/sxxa7g.png" alt="image" title="image"></p><p>接下来选左边的。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/sz3uqz.png" alt="image" title="image"></p><p>开始进入分区过程，等待一下很快就结束了。</p><p>下面就可以选择预装服务了，我将选项全部翻译了放在下方，可以根据自己需要选择安装。我选择了第一项（Web服务）和第三项（MySQL数据库）。这些组件是已经在安装盘内准备好的，不需要联网（联网了也连不到朝鲜自己的"光明网"里的服务器啊😂）</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/t1pzyf.png" alt="db4a675ca27c26fe2b5fd278e66bff8a" title="db4a675ca27c26fe2b5fd278e66bff8a"><br><img src="https://data.imxb.de/pic/i/2026/02/19/t1spys.png" alt="a55e6f3647d17395191ec8c02df2c734" title="a55e6f3647d17395191ec8c02df2c734"></p><p>另外安装了 GUI 支持，使用第一项KDE桌面。（复制文件看起来复制的是gnome啊...）</p><p>开始复制文件，无尽等待...</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/t3jg8a.png" alt="image" title="image"></p><p>安装完成，Enter继续，等待一会，系统将会重新启动。<strong>系统会自动弹出较大的盘，BOOT启动盘并不会弹出，需要手动弹出。否则仍然会被引导进入安装程序。</strong></p><h1>系统优化</h1><p>安装过程就这么“顺利”地结束了。摸索登录，先按唯一的一个选项，接着输入root，输入密码。成功登录到红星OS 主界面。似乎跟 Desktop 版没有什么区别。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/tw4h4g.png" alt="image" title="image"></p><h2>修改系统语言</h2><p>进入终端，运行下面给出的命令：</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/twnelh.png" alt="image" title="image"></p><pre><code>sed -i &#039;s/ko_KP/en_US/g&#039; /etc/sysconfig/i18n</code></pre><h2>允许互联网</h2><p>开发者通过在系统内写入 iptables 规则，避免访问到互联网。</p><p>运行这个命令：</p><pre><code>rm /etc/sysconfig/iptables</code></pre><p>RHEL系操作系统会确认是否要删除，按下y，敲Enter。</p><p>全部完成，<code>reboot</code> 重启系统。接下来系统就会变成英文版，痛苦终于结束了。。吗？事实上，还是有许多地方只支持朝鲜语。</p><h1>试玩</h1><h2>启动web服务</h2><p>红星OS 不支持systemctl命令，只能使用service控制服务状态。安装程序选择地服务默认不会启动，现在我们在终端将其启动。我对 Linux 系统了解不多，service 命令好像不能设置自启动，懒得去搜，欢迎大佬在评论区补充。</p><pre><code>service httpd start # 启动apache服务器
service mysqld start # 启动mysql服务</code></pre><p>在vbox设置中添加转发，浏览器打开127.0.0.1，观察到web服务成功在外部访问到。Server响应头是 <code>Apache/2.2.15 (RedStar 3.0)</code>，挺老的Apache了。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/u344k4.png" alt="image" title="image"></p><p><img src="https://data.imxb.de/pic/i/2026/02/19/u4fuyk.png" alt="image" title="image"></p><h2>补装php</h2><p>查阅文档发现应该安装php5.3的，实际上没有，这边手动补上。<br>系统内没有任何yum源，手动设置一个CD的。记得先把较大的iso （SERVER_rss3_32_key_gui_20131212.iso）挂载上。</p><pre><code>vi /etc/yum.repos.d/cd.repo</code></pre><p>写入以下内容：</p><pre><code>[cd]
name=cd
baseurl=file:///media/CDROM
gpgcheck=0</code></pre><p>ESC :wq 保存。<br>运行安装命令，完成安装，重启httpd后自动生效。</p><pre><code>yum install php
service httpd restart</code></pre><h2>phpinfo</h2><p><img src="https://data.imxb.de/pic/i/2026/02/19/vfdzk1.png" alt="image" title="image"></p><h1>尝试搭建网站</h1><h2>Typecho 1.2.1</h2><p>通过curl命令下载typecho 1.2.1，不知道兼容性。手头上只有这个程序了，先试试吧。</p><pre><code class="lang-bash">curl -H &quot;Host: ******&quot; http://10.241.100.88/typecho.zip -o typecho.zip</code></pre><p>出现报错如下</p><blockquote>Parse error: 문법오유, ':' 는 기대되지 않은 오유입니다., '{' 로 수정하십시오. 파일 /var/www/html/a/install.php 의 31 행에 있습니다.</blockquote><p>看不懂，语法错误，太新了，换！</p><h2>S8FORUM</h2><p>一个很老的论坛系统，大概2000年前后的。相信他能支持老系统。</p><pre><code class="lang-bash">curl http://ajiang.net/luntan/S8forum.zip -o S8forum.zip
unzip S8forum.zip</code></pre><p>发现不支持短标签，编辑PHP.ini。通过phpinfo找到phpini位置为<code>/etc/php.ini</code>。<br>没招了，全都是翻译过的。包括程序的报错。<br><img src="https://data.imxb.de/pic/i/2026/02/19/vk27o1.png" alt="image" title="image"></p><p>vim，通过/short_open_tag定位到配置项，为Off，改为On。<br>重启httpd.</p><pre><code>service httpd restart</code></pre><p>这个系统太老了，是GB2312编码的，httpd默认发了utf8头，修改。编辑如图：</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/vm04tl.png" alt="image" title="image"></p><p>成功建站。</p><p><img src="https://data.imxb.de/pic/i/2026/02/19/vp19rt.png" alt="image" title="image"></p><h1>尾声</h1><p>系统太老了，折腾玩玩，没啥意义。</p><h1>参考</h1><p>[Red Star OS Server installation and change the language to english] <a href="https://www.youtube.com/watch?v=_m57Ueq-vBc">https://www.youtube.com/watch?v=_m57Ueq-vBc</a></p><p>xiobb, 2026.02.19.<br>转载请注明出处。</p>
]]></content:encoded><slash:comments>0</slash:comments><comments>https://www.imxb.de/new_archives/59.shtml#comments</comments><wfw:commentRss>https://www.imxb.de/rss/post.asp</wfw:commentRss></item></channel></rss>