资讯动态

Docker Swarm 节点注册令牌创建失败排查与生命周期管理

发布时间:2026/10/26 20:40:44 来源:尧图企业网站定制
上周某开发者甩过来一张截图问得挺急在一台刚接手的机器上跑docker swarm join-token manager终端只回了一行红字就没了下文节点明明才初始化没多久凭什么创建 enrollment token 就失败这种场景我见过太多次了。enrollment token节点注册令牌在容器集群里是个存在感极低、但一旦卡住就全流程瘫痪的东西它决定了新节点能不能被集群接纳、边缘设备能不能自动纳管、自动化扩缩容脚本会不会在半夜批量炸掉。很多人第一次接触它都是在“节点加不进去”或者“令牌生成报错”的现场而不是在文档里。这篇内容就是把这些现场的东西摊开讲令牌从哪来、签发时为什么会失败、怎么一层层把根因挖出来、怎么管住它的生命周期以及我自己踩过的那些坑。适合正在搭集群的运维、写部署脚本的后端、以及被“节点一直 Pending”折磨过的朋友从没碰过集群的新手到天天和编排打交道的老手都能从里面找到能直接用的东西。1. 先把 enrollment token 这件事掰扯清楚1.1 三种常被叫成“token”的东西先分清再排查排查之前必须做的一件事是把概念对齐。现实里至少有三类东西会被人随口喊成“enrollment token”它们的报错长得很像处理方式却完全不搭界。第一类是Swarm 的 join token。docker swarm init成功之后控制台会吐出一段以SWMTKN-1-开头的长串worker 用它入群manager 用它入群这是最正统的“节点注册令牌”。它由 manager 签发存储在 raft 日志里本质是一段带摘要前缀的密钥。第二类是边缘接入类平台的纳管令牌。某类边缘节点管理平台会在控制台生成一个 enrollment token设备侧 agent 拿着它去控制端换取证书、完成注册之后才建立长连接。这类令牌通常带 TTL比如 24 小时、7 天过期就作废重新生成才管用。第三类是镜像仓库、制品库的访问凭据也有人管它叫 token。这类东西跟集群纳管没关系报错一般出现在docker login或者拉镜像阶段错误码是 401/403。我这篇文章的主线是第一类也就是 Swarm 的 join token因为它最容易被“创建”这个动作卡住报错也最典型。第二类会作为对照讲因为两者在排查手法上有大量可复用的地方。你如果连自己手上是哪种令牌都没分清后面的所有命令都是白敲。提示先执行docker info --format {{.Swarm.LocalNodeState}}输出active是已入群、inactive是未入群、locked是开了自动锁需要解锁。这一步 5 秒能省掉半小时瞎猜。1.2 令牌的签发链路谁签发、存在哪、靠什么校验理解签发链路才能理解报错到底发生在哪一环。Swarm 的 join token 不是在本地文件里“生成”出来的它是一次集群写操作。你在 manager 上敲docker swarm join-token worker守护进程会做这么几件事先确认本节点在 raft 里的角色是 leader 或可达的 manager再向 raft 集群提交一次读请求取出当前令牌最后格式化输出。关键就在这里——“创建”报错的绝大多数情况不是令牌生成失败而是这次集群读写没走通。集群状态不确定、raft 没有 quorum、节点自认为不是 manager、磁盘写不进去都会在“取令牌”这一步暴露成 red herring误导性错误。令牌本身的结构也值得拆一下。以SWMTKN-1-2w3x...-9a8b...为例段位内容作用SWMTKN固定前缀标识这是 Swarm Token1版本号目前恒为 1用于向后兼容中间段密钥的摘要长 24 字符manager 用来快速索引比对不是密钥本体末段密钥本体长 25 字符真正的凭证泄露即等于给了入群资格我做过一个很朴素的对照实验把同一台节点上的令牌复制两份把末段最后一位改掉再拿去docker swarm join立刻被拒说明校验是拿末段做哈希后和中间段比对。反过来只改中间段同样被拒。这就意味着令牌不存在“部分有效”任何一个字符错位都是全盘失败而报错信息往往只说“invalid token”根本不告诉你错在哪一位。复制粘贴多带了个换行、终端自动折行吞了字符、聊天软件把-转成了全角都是真实的翻车原因。还有一点要纠正一个流传很广的误解Swarm 的 join token 默认不会过期。它没有 TTL你三个月前存的令牌今天照样能用直到有人执行--rotate。很多新手看到报错就以为“令牌过期了”跑到网上找续期命令方向从一开始就偏了。真正会过期的是前面说的第二类平台令牌。1.3 为什么偏偏是“创建”这一步报错而不是“加入”时报错这个现象值得单独说因为它直接决定了排查顺序。理论上令牌无效应该在 worker 执行 join 的时候才暴露但现实里大量报错发生在 manager 端执行join-token时原因有三个。一是执行位置错了。docker swarm join-token只能在 manager 上跑而且最好是 leader 或者能连上 leader 的 manager。在 worker 上跑守护进程第一件事就是检查本地角色发现不是 manager直接返回This node is not a swarm manager。这个错误信息看字面意思像是“你还没初始化”很多人于是跑了一遍docker swarm init结果把一个正常运行的 worker 变成了单节点集群——这是最典型的二次事故。二是集群处于不健康状态。manager 之间的 raft 通信断了、quorum 丢了、某个节点卡在Down状态读取令牌这个操作需要集群达成一致达不成就超时。表现出来就是“创建 token 超时”或者“rpc error: code DeadlineExceeded”。三是守护进程本身的状态问题。磁盘满、inode 耗尽、/var/lib/docker所在分区只读、systemd 里配了奇怪的--data-root都会让写操作失败而令牌读取之所以要写是因为它会顺带刷新节点元数据。我通常的排查顺序是先确认角色再确认 quorum最后看守护进程和宿主。这个顺序不是随便定的它是按“出错概率 × 修复成本”排的——角色问题 10 秒能验证磁盘问题要翻日志把便宜的检查放前面永远是对的。2. 报错现场还原五类高频异常的真实含义2.1 权限与角色类This node is not a swarm manager完整报错通常是Error response from daemon: This node is not a swarm manager. Use docker swarm init or docker swarm join to connect this node to swarm and try again.这句话的措辞很有欺骗性它建议你去 init 或 join但真实场景里八成是你在错误的机器上执行了命令。比如你 SSH 到了 web-02 这台 worker以为自己在管理节点上或者用了跳板机跳板机上装的是独立 Docker跟集群毫无关系。验证只需要一条docker info --format {{.Name}} {{.Swarm.LocalNodeState}} {{.Swarm.ControlAvailable}}ControlAvailable为false且LocalNodeState为active说明这是一台 worker命令必须在 manager 上跑。为false且inactive说明它根本没入群。这里我要给一个反直觉的建议不要迷信 “init 一下试试”。在一个已经在集群里跑的节点上执行docker swarm init会得到一个单节点新集群原集群的 overlay 网络、service、secret 全部在本地视图中消失。虽然数据没被删但你要花时间把它退群重加还可能出现 IP 冲突导致的重连失败。稳妥的做法是先看/etc/docker/daemon.json和 systemd unit确认这台机器的定位再去对应的 manager 上操作。如果确实需要在一台机器上查看令牌但不方便登录管理节点可以走另一条路在能被 manager 访问的机器上用DOCKER_HOST指向远端守护进程。但这条路的代价是你要开 2375/2376安全风险极高我一般不建议。更现实的做法是把签发动作收进一个带权限控制的运维脚本里由脚本去连管理节点。2.2 网络与地址类could not choose an IP address to advertise在多网卡机器上初始化集群时这条报错出现频率极高Error response from daemon: could not choose an IP address to advertise since this system has multiple addresses on different interfaces (10.0.3.15 on eth0 and 192.168.56.101 on eth1) - specify one with --advertise-addr它的含义很直白守护进程不知道对外该用哪个 IP。而 advertise-addr 决定了集群里其他节点用什么地址来找你选错的后果不是当场报错而是未来某天新节点 join 超时因为大家拿着一个不可达的地址在敲门。选择逻辑我总结成一句话选那个“所有未来节点都一定能路由到”的地址。具体做法是列一遍候选候选地址类型适用场景风险内网物理网卡地址如 10.x单机房/单 VPC 集群跨 VPC 时可能不通云主机主网卡私网地址同 VPC 多可用区跨账号对等连接需额外配置专用管理网地址有独立管理平面的机房要求所有节点都接管理网公网地址不推荐暴露控制面安全风险高确定之后初始化docker swarm init \ --advertise-addr 10.20.30.11 \ --data-path-addr 10.20.30.11 \ --default-addr-pool 10.99.0.0/16 \ --default-addr-pool-mask-length 24这里--data-path-addr也建议显式指定为同一块网卡它决定 overlay 网络流量走哪条路径。两个参数不一致在多网卡环境下会出现“控制面通、数据面断”的诡异状态表现是 service 起来了但容器互相 ping 不通。--default-addr-pool这一组参数也值得算一下。默认池是10.0.0.0/8切成/24听起来很多但如果你的宿主机网段本身就在 10.x很容易撞车——overlay 网段和物理网段重叠会导致容器访问某些内网服务时被错误路由。我的习惯是给集群单独划一段不常用的私网比如10.99.0.0/16再按节点规模切子网。假设预期最大 200 个 overlay 网络每个 /24 提供 254 个可用地址/16能切出 256 个 /24够用且留有余量。2.3 集群状态类already part of a swarm 与重复注册另一种常见现象是新节点 join 时报Error response from daemon: This node is already part of a swarm. Use docker swarm leave to leave and join another.这说明节点本地还保留着上一个集群的状态。注意这不等于它还在旧集群里。旧 manager 早就被销毁、旧集群彻底不存在了本地状态依然会留着因为状态文件写在/var/lib/docker/swarm/。操作顺序很关键docker swarm leave --force # worker 直接 leavemanager 需要 --force docker info --format {{.Swarm.LocalNodeState}} # 确认为 inactive docker swarm join --token TOKEN MANAGER_IP:2377--force这个参数要谨慎。在 manager 上使用会把节点从 raft 里强制摘除如果摘掉之后存活 manager 数量低于 quorum整个集群会失去写入能力连join-token都读不出来——这正好回到标题那个报错。三 manager 集群 quorum 是 2摘掉一个还剩 2安全两 manager 集群 quorum 也是 2摘掉任何一个立刻失去 quorum这是两节点集群最坑的地方我在第 5 节还会专门说。还有一种更隐蔽的情况节点名重复。两台机器 hostname 都叫node-01第二台 join 进去之后docker node ls里会看到两个同名条目后续基于节点名的调度和脚本全部错乱。join 时加--availability也没用必须改 hostname 或者用--node-name部分版本支持重新注册。这条错误不会在“创建 token”时暴露但会在你拿着令牌批量扩节点时集中爆发。2.4 时钟与证书类令牌没错但校验就是过不去这一类最折磨人因为令牌是好的、网络是通的、角色也对但 join 就是失败报错往往长这样Error response from daemon: rpc error: code Unknown desc x509: certificate signed by unknown authority或者Error response from daemon: Timeout was reached before node joined.根因通常是两个系统时间偏差和CA 证书过期。Swarm 的节点证书默认有效期是 90 天manager 会在到期前自动轮换。但这套机制依赖节点之间能正常通信如果某台节点离线超过证书有效期它回来之后证书已经过期就必须重新 join。更麻烦的是 CA 根证书本身——默认有效期 10 年理论上很久但在一些“克隆虚拟机”场景里克隆出来的机器带着同一份 CA两边都以为自己是对的握手时就会互相不认。时间偏差的排查很朴素# 每台节点上都跑一遍看偏移 date -u # 对比任意一台已知准确的机器超过 5 分钟就要处理我的经验阈值是5 分钟。超过这个量级TLS 握手就可能因为 notBefore/notAfter 判断失败而中断。容器环境下还有个坑某些精简镜像里没有时区数据也没有 NTP 客户端宿主机时间同步正常容器内时间却是漂的如果集群操作在容器里执行一样会出问题。真要轮换 CA命令是docker swarm ca --rotate --cert-expiry 2160h注意--rotate是个破坏性操作执行期间集群会短暂不可用而且旧节点的证书会全部失效需要重新加入。生产环境不要在工作时间做。执行完用docker network ls和docker service ls各看一眼确认 overlay 网络还正常。2.5 存储与配额类raft 日志写不进去令牌自然读不出来这类报错最不直观因为日志里可能只有一句Error response from daemon: rpc error: code DeadlineExceeded desc context deadline exceeded或者Error response from daemon: failed to update cluster: failed to update store: ...真正的原因在宿主层/var/lib/docker所在分区满了、inode 用完了、或者磁盘只剩几百 MB。raft 日志写入需要空间写不进去集群就进入只读甚至不可用状态所有读操作一起超时。排查命令很直接df -h /var/lib/docker df -i /var/lib/docker du -sh /var/lib/docker/* 2/dev/null | sort -rh | head -10常见的大户是容器日志。默认 json-file 驱动不限大小一个话痨服务几天就能吃掉几十 G。治理方式是给 daemon 配日志轮转{ log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }单个容器最多占 300MB200 个容器上限 60GB这个量级需要提前规划盘。改完systemctl reload docker只对新建容器生效存量容器要重建才吃新配置——这一点很多人漏掉改完发现盘还是满的。inode 耗尽更隐蔽df -h显示还有空间df -i已经 100%。小文件多的场景大量镜像层、构建缓存特别容易触发。清理手段是docker image prune -a、docker builder prune但执行前务必确认没有正在跑的流水线在依赖本地缓存。注意磁盘清理属于“事后救火”。真正有效的做法是给/var/lib/docker单独挂盘并在监控里对 80% 阈值告警。集群节点上磁盘满一次代价往往是一整天的排障。3. 从零复现与逐层排查一套能照抄的操作流程3.1 搭一个最小可复现环境排查问题最怕环境不干净。我习惯在任何排障开始前先在一台干净的机器上把场景复现出来。这里用虚拟机或者临时云主机都行要求很简单两块网卡模拟多网卡歧义、一个干净的系统、能装 Docker。准备步骤# 1. 确认系统时间和时区 timedatectl status # 2. 查看网卡与地址记下候选 advertise 地址 ip -4 addr show | grep -E inet |^[0-9] # 3. 查看默认路由判断哪块网卡是主出口 ip route show default第三步很关键。守护进程在选择默认 advertise 地址时会参考默认路由指向的网卡。如果这台机器的主出口是 eth1 而你希望走 eth0就必须显式指定否则初始化出来的集群控制面挂在一条业务不常走的链路上。初始化并记录令牌docker swarm init --advertise-addr 10.20.30.11 # 输出里会包含两段命令分别是 worker 和 manager 的 join 命令如果初始化阶段就报“多地址”错误说明你没指定--advertise-addr回到 2.2 节做地址决策。初始化成功之后这两条 join 命令建议立即存进密码管理器别贴在聊天窗口里——虽然它不会过期但泄露代价很高。3.2 签发令牌的标准动作与参数细节日常最常用的三条命令# 取 worker 令牌 docker swarm join-token worker # 取 manager 令牌 docker swarm join-token manager # 只要令牌本体方便脚本消费 docker swarm join-token worker --quiet--quiet这个参数在自动化流水线里几乎是必用的。完整输出是三行文本直接grep或者awk提取容易在版本升级时翻车--quiet只吐一行原始字符串稳得多。轮换令牌# 轮换 worker 令牌旧令牌立即失效 docker swarm join-token --rotate worker # 同时更新已存在节点上的令牌缓存不没有这回事这里有个必须澄清的点--rotate只影响未来 join 的节点已加入的节点不受影响。它的实现是重新生成密钥并更新集群状态已有节点凭的是各自证书跟 join token 无关。很多人以为轮换会让所有 worker 掉线因此不敢操作结果令牌一直躺在五分钟前的运维群里裸奔。manager 令牌还有额外参数docker swarm join-token manager --quiet # manager 加入时还可以指定监听地址 docker swarm join --token MANAGER_TOKEN \ --advertise-addr 10.20.30.12 \ --listen-addr 10.20.30.12:2377 \ 10.20.30.11:2377--listen-addr决定这台 manager 被其他 manager 访问的地址。多网卡环境下不指定新 manager 可能注册到一个只有自己知道的地址上导致 raft 通信半通不通——表现是docker node ls里节点是Ready但MANAGER STATUS显示Reachable反复跳变。manager 数量规划有个公式quorum floor(N/2) 1。N3 → 2能容忍 1 台故障N5 → 3能容忍 2 台N2 → 2一台都不能挂N4 → 3也是只能容忍 1 台还不如 3 台。所以manager 只配奇数3 或 5这是硬规则。manager 数量quorum可容忍故障评价110测试可用生产别用220最差选择毫无容错321生产最小推荐431性价比低不推荐532大型集群推荐743收益递减通信开销上升3.3 令牌校验解码结构、定位是“生成失败”还是“使用失败”排障时经常要判断一件事到底是令牌本身不对还是集群状态不对。最简单的办法是做结构校验。TOKENSWMTKN-1-xxxxxxxxxxxxxxxxxxxxxxxx-yyyyyyyyyyyyyyyyyyyyyyyyy # 1. 看前缀和段数 echo $TOKEN | awk -F- {print 段数:, NF; print 前缀:, $1-$2; print 摘要长度:, length($3); print 密钥长度:, length($4)} # 2. 检查有没有混入不可见字符 echo -n $TOKEN | od -c | tail -3正常输出应该是 4 段、前缀SWMTKN-1、摘要段 24 字符、密钥段 25 字符。如果段数不对说明复制时被截断或者多带了空格如果od输出里出现\r或\n说明从 Windows 终端复制过来带了回车——这是我见过最冤的一类失败令牌本身完全没问题。校验通过之后再看集群侧# 节点列表看是否有异常状态 docker node ls # 集群信息看 manager 数量和 raft 状态 docker info --format {{json .Swarm}} | python3 -m json.tool # 当前节点角色 docker node inspect self --format {{.Spec.Role}} {{.ManagerStatus.Reachability}}docker info里的Swarm字段包含Managers、Nodes、Cluster三个子结构其中Cluster.ID可以用来确认两台机器是不是同一个集群——同名集群很常见令牌串了集群也是真实事故。3.4 端口与连通性验证三个端口缺一不可很多“令牌没错但 join 不上”的问题最后都是端口没放行。Swarm 依赖三个端口端口协议用途不通时的现象2377TCP集群管理通信join 超时或直接 connection refused7946TCP UDP节点发现与 gossip节点能加入但状态频繁抖动4789UDPoverlay 网络流量容器互通失败service 无法访问验证方式# 从 worker 上验证管理端口 nc -vz 10.20.30.11 2377 # 验证 gossip 端口TCP nc -vz 10.20.30.11 7946 # UDP 端口没法用 nc 直接验证可以看抓包 timeout 5 tcpdump -i any -n udp port 4789 -c 5nc -vz返回succeeded才算通。如果卡在Connection timed out多半是安全组或者防火墙丢包DROP 而不是 REJECT这种情况下 join 报的是超时而不是拒绝方向完全不同。一个实操经验先把 2377 打通再折腾别的。2377 不通后面所有验证都没有意义。我在云上见过安全组只放了 7946 和 4789 的配置因为文档里“节点通信”那段把这俩写得很显眼2377 反而被漏掉。提示overlay 网络跨主机不通时先确认两端内核的overlay模块是否加载lsmod | grep overlay再看 MTU。宿主机网卡 MTU 1500 而 VXLAN 封装后需要 1450链路上有小 MTU 设备时会出现“小包通、大包丢”的经典症状表现为ping通但curl超时。3.5 证书与时间同步的现场处理时间同步这一步我做得比较重。所有集群节点统一配 NTP偏差告警阈值设 1 分钟。处理流程# 查看当前同步状态 timedatectl timesync-status # 强制立即同步 chronyc makestep # chrony ntpd -gq # ntpsec / classic ntp如果节点证书确实过期了处理方式分两种。单个 worker 证书过期最干脆的是退群重加docker swarm leave docker swarm join --token TOKEN MANAGER_IP:2377整个集群的 CA 需要轮换就用docker swarm ca --rotate前面说过它是破坏性操作。轮换之后要检查docker network ls # overlay 网络是否还在 docker service ls # 服务是否正常 docker node ls # 节点状态是否 Ready有一个坑要记住CA 轮换之后之前导出的、被拷到别处用作信任的 CA 证书全部失效如果有外部组件比如某些监控采集器依赖这份证书连集群需要同步更新。这个依赖关系往往没人记录等监控断了才想起来。4. 令牌的生命周期管理与安全实践4.1 轮换策略什么时候必须换换了影响谁令牌的管理原则可以参考密码默认长期有效但要能随时作废。Swarm 的 join token 没有 TTL这既是便利也是风险决定权完全在运维流程上。我给的轮换触发条件有这么几个人员变动有人离职且接触过令牌、令牌曾经出现在日志或聊天记录里、集群规模发生明显变化、以及固定的季度轮换。前三个是被动触发最后一个是被动兜底。轮换动作# 记录轮换时间便于审计 date -u %Y-%m-%dT%H:%M:%SZ /var/log/swarm-token-rotate.log # 轮换 worker 与 manager docker swarm join-token --rotate worker docker swarm join-token --rotate manager轮换的直接影响是旧令牌立即失效。如果此时有自动化脚本正拿着旧令牌扩节点会在几秒内批量失败。所以轮换前要确认扩缩容流水线里没有硬编码令牌配置中心里存的令牌能同步更新边缘设备的重注册能触发。我把这条写进了变更单的检查项轮换令牌 一次涉及所有自动化入口的变更按变更管理走别当日常操作随手敲。4.2 令牌泄露后的处置顺序真遇到泄露顺序错了会放大损失。正确的顺序是先轮换再排查最后复盘。第一步立刻轮换对应角色的令牌docker swarm join-token --rotate worker docker swarm join-token --rotate manager第二步看当前集群里有没有不该存在的节点docker node ls # 重点关注 STATUS 为 Ready 但你不认识的节点 # 以及 AVAILABILITY 为 Active 的陌生条目发现可疑节点# 先排空让它上面的任务迁走 docker node update --availability drain NODE_ID # 再降权如果是 manager docker node demote NODE_ID # 最后移除 docker node rm NODE_ID第三步才是复盘查令牌是从哪个环节漏出去的。常见渠道CI 的日志里set -x把命令打印出来、聊天群里贴的 join 命令、监控采集了环境变量、以及写进 Git 的部署脚本。注意docker node rm之后节点上的容器不会自动清理那台机器还留在集群的 overlay 网络记录里。稳妥做法是先在目标机器上docker swarm leave --force再从管理端rm。顺序反了会留下幽灵条目。4.3 自动化签发脚本要怎么写才不出事规模上来之后手工取令牌不现实都会做成脚本。脚本设计的几个要点每一条都是踩过坑换来的。第一个要点令牌不要进日志。#!/usr/bin/env bash set -euo pipefail MANAGER10.20.30.11 ROLE${1:-worker} TOKEN$(docker -H ssh://ops${MANAGER} swarm join-token $ROLE --quiet) # 校验结构 if [[ ! $TOKEN ~ ^SWMTKN-1-[A-Za-z0-9]{24}-[A-Za-z0-9]{25}$ ]]; then echo 令牌格式异常长度 $(echo -n $TOKEN | wc -c) 2 exit 1 fi # 走安全通道输出不打印 printf %s $TOKEN /run/secrets/swarm_join_token chmod 600 /run/secrets/swarm_join_token这里用了set -euo pipefail管道任何一环失败都会中断避免拿到半截令牌继续往下走。正则校验那一步看起来多余但它能挡住 90% 的复制粘贴类问题——长度不对当场就报不用等到 join 失败再回头查。第二个要点通过DOCKER_HOST走 SSH 而不是开 TCP 端口。上面脚本里用的ssh://形式Docker 会走 SSH 通道转发不需要在管理节点上暴露 2376也不需要维护 TLS 证书。代价是执行用户要有 SSH 权限这个用专用账号 命令白名单控制。第三个要点令牌的传递用文件或环境变量不要用命令行参数。命令行参数会出现在ps aux里同机器上的其他用户能看到。这个细节在单机环境下无所谓在共享跳板机上就是实打实的泄露通道。第四个要点幂等。脚本可能被重跑要保证重复执行不会把集群搞乱。比如轮换类操作要加锁exec 9/var/lock/swarm-token-rotate.lock flock -n 9 || { echo 已有轮换任务在执行; exit 0; }5. 常见问题速查与踩坑实录5.1 报错速查表把前面散落的现场整理成一张表方便对着查。报错关键字最可能的根因一条验证命令处理方向This node is not a swarm manager在 worker 或未入群节点执行docker info --format {{.Swarm.ControlAvailable}}换到 manager 执行could not choose an IP address多网卡未指定地址ip -4 addr show加--advertise-addr重新 initalready part of a swarm本地残留旧集群状态docker info --format {{.Swarm.LocalNodeState}}docker swarm leave --force后重加context deadline exceededquorum 丢失或 raft 卡住docker node ls恢复多数派 managerx509: certificate signed by unknown authorityCA 不一致或证书过期date -u对比各节点校时 /ca --rotateno space left on device磁盘或 inode 满df -h、df -i清理日志与镜像connection refused到 2377端口未放行或服务未监听nc -vz ip 2377放行安全组 / 查监听Timeout was reached before node joined网络不通或时钟偏差nc -vzdate -u先通网再校时invalid token令牌截断、带换行、跨集群od -c看不可见字符重新完整复制node is not available节点被 drain 或离线docker node ls--availability active5.2 我自己踩过的几个坑坑一两节点集群的死亡螺旋。早期给一个小项目搭集群图省事只配了两个 manager。某次升级重启其中一台剩下那台立刻失去 quorumdocker node ls一直卡住不返回join-token直接超时——就是标题里那个报错。当时判断失误以为是令牌库坏了差点把 raft 数据目录删了重建。后来才想明白N2 的 quorum 是 2任何一台失联都等于集群失去写入能力。加了第三台之后一切正常。从那以后我的规则是manager 要么 1 台纯测试要么 3 台起两台的配置看都不看。坑二克隆虚拟机导致的双胞胎节点。用模板批量开机器忘了改机器标识结果三台新节点在集群里显示同一个 hostname。表面看都Ready实际调度混乱某些服务被反复拉起又杀掉。docker node ls里能看到重复条目但不容易注意到因为 ID 是不同的。排查花了不少时间最后靠docker node inspect id --format {{.Description.Hostname}} {{.Status.Addr}}逐台对比才定位。现在我的初始化脚本第一步就是改 hostname 并重启 docker。坑三令牌进了 CI 日志。有次扩节点脚本里写了set -x用于调试忘了删令牌被完整打进流水线日志保留期 90 天。发现之后按 4.2 的流程走了一遍轮换、清节点、改脚本。这件事之后我把“禁止在涉及令牌的脚本里开set -x”写进了团队规范并且给日志加了一条正则扫描告警匹配SWMTKN-1-前缀。这条告警后来还真的又抓到过一次。坑四磁盘满了但df -h显示还有 20%。这个坑最迷惑。现象是集群各种超时日志里全是context deadline exceeded。df -h看还有空间以为是网络问题查了两小时。最后df -i一看 inode 100%因为镜像层小文件太多。清理完docker builder prune和旧镜像才恢复。从此我的巡检脚本里df -i和df -h同等重要。5.3 预防性巡检把问题挡在报错之前报错是结果巡检是提前量。我维护的巡检脚本跑这么几项每天一次异常就告警。#!/usr/bin/env bash set -uo pipefail echo 集群状态 docker info --format LocalNodeState{{.Swarm.LocalNodeState}} ControlAvailable{{.Swarm.ControlAvailable}} Managers{{.Swarm.Managers}} Nodes{{.Swarm.Nodes}} echo 节点健康 docker node ls --format {{.ID}}\t{{.Hostname}}\t{{.Status}}\t{{.ManagerStatus}} echo 磁盘 df -h /var/lib/docker | tail -1 df -i /var/lib/docker | tail -1 echo 时间偏差秒 date -u %s echo 错误日志近 50 条 journalctl -u docker --since 24 hours ago -p err --no-pager | tail -50重点看三处Managers数量是否等于预期、节点Status里有没有非Ready、磁盘两个维度的使用率。时间偏差那一行需要跨节点对比单独一台看不出问题所以脚本里只打时间戳由采集端做差值计算。告警阈值我给的是磁盘使用率 80%、inode 使用率 80%、manager 数量低于预期即告警、任意节点Down超过 5 分钟告警。这几条覆盖了前面五类报错里的大部分前置信号。最后一句话是我自己的体会enrollment token 这个报错看起来是令牌问题实际十次里有八次跟令牌本身没关系。先看角色、再看 quorum、然后看磁盘和时间这个顺序比任何文档都管用。真正需要背下来的只有一条——令牌没有过期这一说报错说它无效那多半不是它的问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑