跳到主要内容

11 篇博文 含有标签「Docker」

查看所有标签

Airflow 3 密码改了不生效?真源是 passwords.json 而非数据库

· 阅读需 5 分钟

在 Airflow 3 上把用户密码更新之后,用新密码请求 /auth/token 仍返回 401——而数据库里的密码哈希确实已经改成了新值。

在开发 AI运营 时遇到此问题——基于大语言模型的智能分析,自动洞察市场趋势、用户行为、销售数据,提供精准运营策略。平台的 DAG 触发链路依赖这套 Airflow 的 JWT 鉴权,密码轮换卡住,管道授权跟着停摆。

问题现象:密码改了,/auth/token 还认旧密码​

新密码请求 /auth/token 返回 401、旧密码仍返回 201——修改动作每一步都「成功」,实际生效的始终是旧密码。这次轮换的场景是安全处置:旧密码已泄露,必须立刻换掉,所以「改了但没生效」的每一分钟都在裸奔。

第一反应是用官方 CLI 重置:

$ airflow users reset-password -u apiuser -p <new>
AttributeError: 'AirflowSecurityManagerV2' object has no attribute 'find_user'

报错指向 FAB(Flask-AppBuilder)认证体系的安全管理器,看起来像 3.x 的版本 bug。既然 CLI 坏了,那就绕过它直接改数据库——这步走岔了,为后面更深的迷惑埋了伏笔。

根因:SimpleAuthManager 不读数据库,密码在 passwords.json​

这套部署的认证管理器不是报错里暗示的 FAB,而是 SimpleAuthManager——用一条命令就能看到真相:

airflow config get-value core auth_manager
# airflow.api_fastapi.auth.managers.simple.simple_auth_manager.SimpleAuthManager

理清这三层,现象就完全解释得通了:

  • CLI 报错是假导。airflow users 系列 CLI 属于 FAB 认证体系;SimpleAuthManager 部署上跑它,内部代码路径直接撞上 AttributeError。报错里的 AirflowSecurityManagerV2 类确实存在(作为遗留组件),但跟当前生效的认证管理器无关。
  • ab_user 表是遗留数据。从 Airflow 2.x 升级上来的部署,库里留着 FAB 时代的用户表。用 SQLAlchemy 直写 UPDATE ab_user SET password=... (werkzeug generate_password_hash 生成 scrypt 哈希),返回 rowcount=1,再用 check_password_hash 验证——新密码与库里的哈希完全匹配。可 /auth/token 照旧 401:表是真改了,只是认证流程根本不读它。
  • 真源是一个 JSON 文件。SimpleAuthManager 的用户密码来自 AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_PASSWORDS_FILE 指向的 passwords.json,内容就是扁平映射:
{
"apiuser": "<密码>",
"admin": "<密码>"
}

用户-角色对应关系则由 AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_USERS(形如 apiuser:admin,admin:admin)声明。排查这类问题的第一步应该是确认「谁在管认证」,而不是急着修「认证坏了」的表象。

解决方案:改 passwords.json 后必须 force-recreate 容器​

三步:

  1. 改宿主机上的 passwords.json(compose 挂载进容器的那个源文件):
python3 - <<'EOF'
import json
d = json.load(open('/root/workspace/ai_dag/deploy/passwords.json'))
d['apiuser'] = '<新密码>'
json.dump(d, open('/root/workspace/ai_dag/deploy/passwords.json', 'w'), indent=2)
EOF
  1. 重建 api-server 容器。这一步不可省——密码文件仅启动时读取,改文件对运行中的进程没有任何影响:
cd /root/workspace/ai_dag/deploy
docker compose up -d --force-recreate airflow-api-server
  1. 等服务就绪后双向验证——新密码必须通过、旧密码必须失效,两个断言缺一不可:
# 探活:重建后约半分钟内恢复 200
curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/api/v2/monitor/health

# 新密码 → 期望 201
curl -s -o /dev/null -w '%{http_code}' -X POST http://localhost:8080/auth/token \
-H 'Content-Type: application/json' \
-d '{"username":"apiuser","password":"<新密码>"}'

# 旧密码 → 期望 401
curl -s -o /dev/null -w '%{http_code}' -X POST http://localhost:8080/auth/token \
-H 'Content-Type: application/json' \
-d '{"username":"apiuser","password":"<旧密码>"}'

实测结果:新密码 201、旧密码 401,轮换闭环。只验「新密码能用」不验「旧密码失效」是这类操作最常见的收尾漏洞。

注意事项

  • 先确认认证管理器再动手:airflow config get-value core auth_manager 的输出决定密码改在哪——FAB 在数据库,SimpleAuthManager 在文件,两者路径完全不同。
  • 改密码文件必须重建容器:文件仅启动时读取,docker compose restart 不会让它重新加载。
  • 依赖该认证的服务要同步换新密码并重启加载,否则管道在「旧密码 401、新密码未接线」的窗口里全断。
  • 重建 api-server 有秒级到半分钟的服务中断,挑低峰执行,探活循环等 health 回 200 再做验证。

常见问题​

Airflow 2 升级到 3 后重置密码的命令为什么报错?​

3.x 部署若使用 SimpleAuthManager,FAB 的 airflow users 系列 CLI 不再可用(实测报 AttributeError: 'AirflowSecurityManagerV2' object has no attribute 'find_user')。先跑 airflow config get-value core auth_manager 确认实际认证管理器,再决定改数据库还是改文件。

SimpleAuthManager 的用户密码存在哪?​

存在 passwords.json 文件里(flat JSON,形如 {"用户名": "密码"}),路径由 AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_PASSWORDS_FILE 指定。文件仅容器启动时读取,改完必须 docker compose up -d --force-recreate airflow-api-server 才生效。

为什么直接更新 ab_user 表的密码哈希不生效?​

ab_user 是 FAB 认证管理器的用户表。从 2.x 升级到 SimpleAuthManager 的部署里它是遗留数据,认证流程根本不读它——实测 rowcount=1 写入 scrypt 哈希成功,/auth/token 照旧返回 401。

CCLEE

独立开发者,24年电商行业实战经验,专注将AI能力落地于真实商业场景。

合作咨询

跨容器连 PostgreSQL 报 fe_sendauth?exec 免密≠TCP 免密

· 阅读需 5 分钟

在 DB 容器所在宿主机上 docker exec 进容器跑 psql,免密直连一切正常;同样的用户名换到另一个容器里走 TCP 连接,直接报 fe_sendauth: no password supplied。

在开发 AI运营 时遇到此问题——基于大语言模型的智能分析,自动洞察市场趋势、用户行为、销售数据,提供精准运营策略。编排容器里的运维脚本要跨容器访问分析数据库,连接串照搬了 exec 习惯,第一跳就摔在认证上。

问题现象:exec 能连,TCP 报 fe_sendauth: no password supplied​

同一台宿主机、同一个数据库、同一个用户,两种连法两种命运。实测三方对照(Airflow 容器 → PostgreSQL 容器):

连接方式结果
docker exec cclhub-db psql -U postgres(容器内 socket)免密直连成功
跨容器 TCP,DSN 不带密码fe_sendauth: no password supplied
跨容器 TCP,DSN 带密码连接成功

如果你搜的是「fe_sendauth no password supplied」「psql 不带密码报错」「docker 连 postgres 免密失败」,都是这一类。

根因:socket 免密与 TCP 密码认证是两条 pg_hba 路径​

PostgreSQL 的认证由 pg_hba.conf 按连接类型逐条匹配,官方镜像的默认配置对两条路径给的是完全不同的规则:

  • 容器内 docker exec 走 Unix domain socket,对应 local all all trust 一类的规则——信任本地 socket,不问密码;
  • 跨容器走 TCP(host 类型规则),官方镜像默认要求 scram-sha-256 / md5 密码认证。

于是「exec 免密」建立起来的连接习惯,到了 TCP 上就是缺密码——fe_sendauth: no password supplied 的字面意思正是「服务端要密码,客户端一个都没给」。

顺带把两个容易混淆的错误分开:no password supplied 是没带密码,password authentication failed 是带了但错了。前者查连接串里有没有 password,后者查密码对不对——对症的药不同。

解决方案:DSN 带密码,别照搬 exec 的连接习惯​

跨容器脚本统一用带密码的连接串:

postgresql://postgres:<密码>@<db 主机>:5432/<库名>

密码来源推荐直接读 DB 容器的环境变量(官方镜像的 POSTGRES_PASSWORD 就是为初始化设置的),避免二次硬编码:

PW=$(docker exec cclhub-db printenv POSTGRES_PASSWORD)
psql "postgresql://postgres:${PW}@localhost:5432/postgres" -c "SELECT 1"

Python/应用侧同理,DSN 进环境变量管理:

import os
import psycopg

DSN = os.environ["APP_DB_DSN"] # postgresql://user:pass@host:5432/db
with psycopg.connect(DSN) as conn:
conn.execute("SELECT 1")

注意 URL 里的密码若含 @ : / # 等保留字符需要百分号编码——又一个特殊字符咬人的场景,生成数据库密码时规避这类字符能省掉一整类麻烦。

边界与变体​

  • 报错形态随客户端变:libpq 交互式终端(不带 -t 的 psql)会先提示 Password for user postgres: 再因无输入报 fe_sendauth;连接池、GUI 工具(如 pgAdmin)则直接在界面标认证失败——错误文本不同,根因相同。
  • .pgpass 文件与 PGPASSWORD 环境变量是 DSN 之外的两种供密方式,容器场景里 DSN/env 注入比挂 .pgpass 文件更常见。
  • pg_hba 允许改成 trust 让 TCP 也免密——技术上可行,生产环境不要做:任何能触达端口的进程都拿得到无凭据访问。
  • 认证方法版本差异:新版本官方镜像默认 scram-sha-256,老客户端库可能不支持——那是另一类错误(认证方法不支持),与本文的「没带密码」不同。

注意事项

  • 排障先看连接路径:socket 还是 TCP、命中 pg_hba 哪条规则,决定了要不要密码——别把 exec 的行为当成全局行为。
  • 错误语义要分清:no password supplied(没带)与 authentication failed(带错)的修复动作完全不同。
  • DSN 里的密码做特殊字符规避或百分号编码,URL 保留字符会静默改变解析结果。

常见问题​

fe_sendauth: no password supplied 是什么意思?​

服务端 pg_hba.conf 要求密码认证,但客户端连接串里没有提供密码。它不是「密码错误」(那是 authentication failed),是「没带密码」——检查 DSN 是否缺 password 部分。

为什么 docker exec 进容器 psql 不用密码就能连?​

容器内默认走 Unix domain socket,pg_hba 对本地 socket 配置了 trust 免密;跨容器是 TCP 连接,命中要求密码的 host 规则。同一套命令换个连接路径,认证要求完全不同。

容器里脚本连 PostgreSQL 的正确姿势是什么?​

DSN 显式带密码,如 postgresql://user:password@host:5432/db。密码从 DB 容器的 POSTGRES_PASSWORD 环境变量读取注入,不要硬编码,也不要指望 socket 免密在 TCP 上生效。

CCLEE

独立开发者,24年电商行业实战经验,专注将AI能力落地于真实商业场景。

合作咨询

Docker 卷挂载后是空的?etcd 数据全在可写层,容器重建即丢

· 阅读需 6 分钟

磁盘清理时用 docker ps -as 巡检各容器可写层,etcd 容器赫然 385M——而它挂载的数据卷 etcd_data 里只有 8K。当时磁盘清理没敢动它:一旦 recreate,Milvus 的全部元数据就随可写层一起蒸发。

在开发 AI客服 时遇到此问题——7×24小时AI客服,快速解答产品使用问题;它的知识库检索跑在 Milvus 上,而 Milvus 的元数据全靠这个 etcd。

TL;DR​

compose 的 volumes: 只负责「把卷挂到容器某个路径」,应用往哪写由它自己的参数决定。etcd 没设 data-dir 时默认写到工作目录下的 default.etcd——挂载点 /etcd 它根本没用,385M 数据全在可写层,docker compose up -d 的任何一次重建都会把它带走。修复用 etcd 原生迁移路径:在线快照 → restore → 停机换数据 → 补 ETCD_DATA_DIR → 带卷重建,零丢失,全程停机不到两分钟。

问题现象​

compose 里的 etcd 服务看起来是「正确」的——卷挂了:

  etcd:
image: quay.io/coreos/etcd:v3.5.5
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
# ... 没有任何 data-dir 相关配置
volumes:
- etcd_data:/etcd

但两个数字对不上:

$ docker ps -as --format "{{.Names}}\t{{.Size}}" | grep etcd
rag-service-etcd-1 385MB (virtual 199MB) ← 可写层 385M

$ docker exec rag-service-etcd-1 du -sh /etcd
8K /etcd ← 挂载的卷是空的

$ docker exec rag-service-etcd-1 ls -la / | grep etcd
drwx------ 3 root root 4096 default.etcd ← 数据在这:容器根目录

根因​

挂载 ≠ 使用。 volumes: etcd_data:/etcd 只保证「卷被挂到 /etcd 这个路径」,至于 etcd 往哪写,由它自己的 --data-dir 参数决定。etcd 的缺省 data-dir 是工作目录下的 default.etcd——这份 compose 既没设 ETCD_DATA_DIR 环境变量,也没传 --data-dir 命令行参数,于是 etcd 在容器根目录自建了 default.etcd,把全部数据写进了可写层。挂载点 /etcd 从第一天起就是空的。

这类「卷是摆设」最阴险的地方在于平时完全无症状:服务正常跑、数据正常读写、监控全绿。只有当你需要 docker compose up -d --force-recreate(换配置、回收可写层、迁移宿主机)的那一刻,可写层被整体丢弃,数据才消失——而那通常是你在做紧急运维、最不需要第二起事故的时候。

解决方案​

用 etcd 原生的快照迁移路径,在线快照保证一致性,短暂停机只出现在最后的数据切换。

步骤 1:在线一致性快照​

docker exec rag-service-etcd-1 sh -c \
'ETCDCTL_API=3 etcdctl --endpoints=http://127.0.0.1:2379 snapshot save /tmp/etcd-snap.db'
docker cp rag-service-etcd-1:/tmp/etcd-snap.db /root/etcd-snap.db

snapshot save 对运行中的 etcd 是安全的(不走文件复制,走 Raft 后端快照),不停机、不锁写。

步骤 2:restore 成目标 data-dir 结构​

docker run --rm -v /root:/host quay.io/coreos/etcd:v3.5.5 \
etcdctl snapshot restore /host/etcd-snap.db --data-dir /host/etcd-restored

restore 会生成带完整 member/ 结构的数据目录(快照本身不能直接当 data-dir 用)。

步骤 3:停机切换,数据入卷​

docker stop rag-service-etcd-1
rm -rf /var/lib/docker/volumes/etcd_data/_data/* # 卷是空的,清掉挂载残留
cp -a /root/etcd-restored/. /var/lib/docker/volumes/etcd_data/_data/

步骤 4:补上缺失的配置,带卷重建​

  etcd:
environment:
- ETCD_DATA_DIR=/etcd # ← 缺的就是这一行
# ...
volumes:
- etcd_data:/etcd
docker compose up -d etcd    # 配置变更触发 recreate,新容器以卷为 data-dir

步骤 5:验证​

docker exec rag-service-etcd-1 etcdctl --endpoints=http://127.0.0.1:2379 endpoint health
du -sh /var/lib/docker/volumes/etcd_data/_data # 数据应在卷里
docker ps -as | grep etcd # 可写层应回落到 KB 级

本次修复后:可写层 385M → 8KB,数据落卷 123M,上游 Milvus 自动重连、元数据读写正常。

注意事项

  • 给 stateful 容器(etcd/postgres/redis/minio)做巡检时,把「可写层大小 vs 卷大小」当固定对账项:docker ps -as 的 SIZE 一栏异常膨胀而卷很空,几乎必然是数据没落卷。
  • 判断「数据在哪」要追进程实际读写的路径(docker top 看参数、进容器找数据目录),不能只看 compose 有没有 volumes 行——有挂载不等于被使用。
  • 单节点 etcd 的快照 restore 会重建 member 元数据,只适用于单节点场景;多节点集群的迁移请走成员变更流程。
  • 同类排查方法论见 容器日志吃满服务器磁盘?docker system df 的 reclaimable 是误报——docker ps -as 可写层观察是同一把刀;挂载路径被覆盖的另一形态见 Docker Volume 覆盖 Bind Mount。

常见问题​

Docker 挂载卷后目录是空的怎么回事?​

两种常见原因:一是空卷挂载遮盖了镜像内原有目录(Docker 的既有行为);二是应用的数据目录参数根本没指向挂载点,数据写进了容器可写层——本文的 etcd 案例就是这种。用 du 分别看卷目录和容器可写层的大小,一眼可辨。

etcd 数据怎么安全迁移到新的 data-dir?​

etcdctl snapshot save 在线做一致性快照(不停机),etcdctl snapshot restore --data-dir 生成带 member 结构的目标目录,停容器后把数据放到新位置、以新 data-dir 启动,最后 endpoint health 加上游重连验证。全程停机只在切换那一段。

compose 里挂了卷为什么没生效?​

volumes: 只把卷挂到容器的某个路径;应用写哪里由它自己的配置决定——etcd 看 data-dir,postgres 看 PGDATA,redis 看配置文件里的 dir。挂载点和应用配置对不上,卷就只是个空目录,数据全在可写层,重建即丢。

CCLEE

独立开发者,24年电商行业实战经验,专注将AI能力落地于真实商业场景。

合作咨询

容器日志吃满服务器磁盘?docker system df 的 reclaimable 是误报

· 阅读需 7 分钟

告警邮件:生产服务器根分区用到 81%(30G/40G,超过 80% 红线)。第一反应是查 docker system df——它显示 images 一栏 RECLAIMABLE 100% (7),看起来删掉镜像就能回血;但这 7 个镜像全在运行,真按提示 docker image prune -a 就是生产事故。

在开发 AI运营 时遇到此问题——基于大语言模型的智能分析平台,自动洞察市场趋势、用户行为与销售数据;出问题的是跑它 Airflow 数据管道的 Docker 服务器。

TL;DR​

磁盘超红线时别信 docker system df 的 RECLAIMABLE——它是「无容器引用空间」的估算口径,active 镜像照样标 100%。正确姿势:sudo du -xh -d1 / 逐层找大头。这次的三大隐形消耗都不在业务数据里:容器可写层里无限累积的 task 日志(Dockerfile 没给日志配卷)、包管理器缓存(npm + pnpm 共 ~4G)、只增不删的备份脚本(每次全量克隆从不清理)。对应清理:缓存直删、可写层 force-recreate 回收、备份脚本加 TTL 剪枝——81% 回落到 64%。

问题现象​

$ df -h /
Filesystem Size Used Use% Mounted on
/dev/vda1 40G 30G 81% /

$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 7 7 4.2GB 100% (7) ← 全是运行中镜像
Containers 5 5 810MB 0%

按 docker system df 的提示去做文章(清理镜像)无从下手——RECLAIMABLE 100% 但 ACTIVE 也是 7/7。真正的空间去哪了,docker system df 完全没体现:

sudo du -xh -d1 / | sort -rh | head
# 部分输出:
# 2.7G /root/.npm ← npm 缓存
# 1.1G /root/.local/share/pnpm ← pnpm store
# 857M /root/backups-git ← 备份工作区,只增不删
# (overlay2 内藏:airflow scheduler 可写层 468M + dag-processor 257M)

根因​

三类消耗在「业务数据」视角里全部隐形。

容器可写层吃日志。 Airflow 的 task 日志写在容器内部、没挂外置卷——scheduler 可写层 468M、dag-processor 257M,约两周累积 780M,随时间单调增长。镜像层不变,变的都是可写层;docker system df 把它归在 Containers 的 SIZE 里,混在 810M 的总数里毫无存在感。

包管理器缓存只进不出。 频繁部署/构建的服务器上,~/.npm(2.7G)和 pnpm store(1.1G)持续累积,没有人会主动去清。

备份脚本只增不删。 备份脚本每次全量克隆仓库工作区推 GitHub,旧的克隆目录从不剪枝——857M 里大部分是历史重复。更隐蔽的是孤本:某些备份目录对应的分支从未推送成功,直接删有风险,需要先核对。

而 docker system df 的 RECLAIMABLE 是按「没有运行容器引用」估算的统计口径,active 镜像也可能显示 100% reclaimable(本次 7/7 全如此)——它是误报来源,不是清理依据。

解决方案​

步骤 1:du 逐层定位,先看再删​

sudo du -xh -d1 / | sort -rh | head        # 根分区逐层
sudo du -xh -d1 /var/lib/docker | sort -rh | head # Docker 目录下钻
docker ps -as --format "table {{.Names}}\t{{.Size}}" # 各容器可写层

du -x 不跨文件系统,避开 proc/sys 噪音和 overlay 混淆;docker ps -as 的 SIZE 列直接暴露每个容器的可写层大小——这是定位「日志写进容器」类问题的关键一条。

步骤 2:缓存直删​

npm cache clean --force        # 或直接 rm -rf ~/.npm/_cacache
pnpm store prune # 只清未引用的包

缓存类共回收 ~5.1G,无风险、可随时重下。

步骤 3:可写层 force-recreate 回收​

docker compose up -d --force-recreate   # 可写层随旧容器删除而回收

本次回收 ~780M。两个前提:选低峰期(服务会重启);容器内还有用要先捞出来——比如 docker cp 把 task 日志拷出,否则随容器一起没了。

步骤 4:备份脚本加 TTL,防复发​

# 备份目录保留 7 天,超过自动剪
find /root/backups-git -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +

把剪枝加进备份脚本尾部(本例 github-backup-push.sh 已部署);删除前核对未推送的孤本分支(git ls-remote 对比),确认无独有提交再删——本次 325M 孤本经确认后清理。

清理完成后:81% → 64%,缓存/剪枝/可写层三处合计回收 ~6.7G。

注意事项

  • docker system df 的 RECLAIMABLE ≠ 可删空间:active 镜像也可能标 100%(本次 7 个全在用)。清理决策以 du + docker ps -as 实测为准。
  • force-recreate 会重启服务且回收可写层——需要留存的日志先 docker cp 出来;根治是给日志配外置卷 + 保留期轮转,可写层回收只是这一次的止血。
  • 删备份目录前必核对:有没有从未推送成功的孤本分支,删之前 git ls-remote 确认。
  • 磁盘红线告警要配「定位命令」一起给,收到告警的人第一件事是 du -xh -d1 /,而不是猜。
  • 同一台服务器更早的「磁盘 93% + CPU 160%」排查复盘见 2 核 7G 服务器 Docker 资源黑洞三步排查。

常见问题​

docker system df 的 RECLAIMABLE 显示 100% 能直接删吗?​

不能。RECLAIMABLE 是「当前无容器引用」空间的估算口径,运行中的 active 镜像也可能被标 100% reclaimable——实测 7 个镜像全在用仍显示如此。它回答的是「理论上有多少未被引用」,不回答「能不能删」。动手前用 du -xh -d1 和 docker ps -as 实测定位。

docker ps 的 SIZE 和 docker system df 的 SIZE 有什么区别?​

docker ps -as 的 SIZE 是单个容器可写层的大小;docker system df 是镜像/容器/卷/缓存的分类汇总。容器内日志无限累积只体现在可写层(docker ps -as 能看到),不会体现在任何 image size 上——这就是「镜像看起来不大、磁盘却在涨」的原因。

Docker 服务器磁盘满了怎么清理?​

分三类:包管理缓存直删(npm cache clean --force、pnpm store prune),无风险;容器可写层用 docker compose up -d --force-recreate 回收(会重启服务,日志先备份);备份、日志、克隆目录这类加 TTL 定期剪枝防复发。任何删除动作前,先用 du -xh -d1 / 确认大头位置。

CCLEE

独立开发者,24年电商行业实战经验,专注将AI能力落地于真实商业场景。

合作咨询

Docker Compose 服务重启后起不来?检查 restart 策略

· 阅读需 5 分钟

在 RAG 知识库项目中排查依赖 Milvus 的服务启动失败,以下是完整排查过程。

TL;DR​

宿主机重启(或容器崩溃)后,一组服务没有自动恢复,应用端口无监听、docker ps -a 里容器全是 Exited。根因是 docker-compose.yml 没配 restart 策略(默认 no),容器挂了就永远躺着。解法:给所有生产服务加 restart: always,让基础设施在崩溃或重启后自愈。

WSL2 + Docker 两个网络坑:端口被静默占用 & host 模式 localhost 不通

· 阅读需 5 分钟

TL;DR​

WSL2 + Docker Desktop 有两个常见的网络坑:

  1. 端口被静默占用:Docker 容器映射 5432 后,SSH 隧道 localhost:5432 连到的是容器内的 PostgreSQL 而非远程服务器——密码没错,连的实例错了
  2. host 模式 localhost 不通:network_mode: host 共享的是 Docker 工具 VM 网络,不是 WSL2 网络——curl localhost:8080 失败

容器端口在 WSL2 里访问不到?Docker Desktop 的 network_mode:host 陷阱

· 阅读需 4 分钟

在为客户搭建 AI 数据分析平台(Airflow + PostgreSQL)的本地开发环境时遇到此问题,记录根因与解法。

TL;DR​

Docker Desktop for Windows (WSL2 backend) 下,network_mode: host 的容器端口无法从 WSL2 宿主机访问。容器内 ss 显示端口在监听,但宿主机 curl localhost:PORT connection refused。原因是 host 模式共享的是 Docker 内部工具 VM 的网络,不是 WSL2 的网络。解法:override 文件中用 network_mode: !reset 移除 host 模式,改用 bridge + external network + 端口映射。

修改 WordPress Block Theme 不生效?FSE 开发 5 大难题排查指南

· 阅读需 8 分钟

在为客户开发 WordPress Block Theme 时反复遇到这五个问题,每次排查都花了不少时间。整理成指南,帮助同样在做 FSE 开发的同学快速定位。

TL;DR​

五个问题按频率排序:文件修改不生效(数据库缓存覆盖文件)、块嵌套错乱(注释未关闭)、子主题内容不渲染(缺少 post-content 块)、SVG 图标消失(WP_Filesystem 被插件污染)、WP-CLI 邮件失败(SMTP 插件在命令行不生效)。每个场景都给出可直接复用的排查命令。