Kubernetes节点磁盘告警:镜像日志清理与kubelet回收

发布时间:2026/10/27 23:50:52
Kubernetes节点磁盘告警:镜像日志清理与kubelet回收 1. 节点磁盘告警的现场先搞清楚谁在吃盘凌晨两点被 NodeDiskPressure 的告警叫起来这种经历做过几年 Kubernetes 运维的人基本都有一两次。登录节点一看df -h根分区 98%kubectl describe node里 Conditions 挂着DiskPressure True紧接着就是 Pod 被 kubelet 驱逐、新 Pod 调度不上去、业务方在群里问为什么服务一直在重启。集群节点磁盘清理这件事看起来只是运维操作里的一个体力活真落到生产环境里它牵扯的是容器运行时、kubelet 驱逐策略、日志采集链路、存储卷生命周期四条线的交汇点。这篇文章想聊的就是这个场景一个正在跑业务的 Kubernetes 集群某个或某几个节点的磁盘水位突然飙到危险区你需要在最短时间内定位占用来源、安全地回收空间、并且把下次还会满这个问题压下去。适合已经能熟练敲kubectl get pods、但对节点层面的磁盘构成还比较模糊的同学也适合那些已经被磁盘告警折腾过几轮、想整理一套标准动作的老手。先说结论性的判断绝大多数 Kubernetes 节点的磁盘不是被业务数据撑爆的而是被容器运行时垃圾、容器日志、以及一堆以为已经删了其实还占着的文件撑爆的。搞清楚这个前提后面的排查方向就不会跑偏。下面按为什么会满 → 怎么定位 → 怎么清 → 怎么防 → 怎么避坑的顺序展开每一步都给出可以直接抄的命令和参数。1.1 从一次典型的 DiskPressure 告警说起某公司的业务集群二十多个工作节点跑着几百个 Pod日志统一走 sidecar 采集。某天开始连续三天都有节点报磁盘压力而且都是同一批节点——跑离线计算任务的那几台。运维同学的第一反应是日志太多了于是登上去rm -rf /var/log/pods/*磁盘确实降下来了但第二天又满了。这个案例里其实藏着三个信息点值得单独拎出来讲。第一告警集中出现在同一批节点说明问题是负载特征而不是全局配置。离线任务的特点是大镜像、频繁拉取、短生命周期 Pod 多这三条正好对应镜像层堆积和容器日志暴增两个占用源。第二rm -rf /var/log/pods/*能生效说明日志确实是主要占用之一但它只是当下能看到的部分镜像层和其他缓存没动所以第二天必然复发。第三清理动作本身有风险。直接删/var/log/pods下的文件会导致正在运行的容器日志句柄悬空日志采集组件读不到新内容业务侧排查问题时会发现日志断档。安全做法是truncate而不是rm这一点后面会细说。所以节点磁盘清理不是一条命令的事它需要一套先看清账本、再分类处理、最后固化策略的流程。1.2 节点磁盘的四大占用来源在动手之前先把一个 Kubernetes 节点上的磁盘占用拆成可管理的四块这是后面所有排查动作的基础框架。占用来源典型路径增长特征清理难度容器镜像与运行时层/var/lib/containerd、/var/lib/docker随镜像拉取次数阶梯式增长低可用运行时命令回收容器日志/var/log/pods、/var/log/containers随业务日志量线性增长中需注意句柄与采集链路可写层与临时卷/var/lib/kubelet/pods/*/volumes随 Pod 生命周期累积中需确认 Pod 已彻底删除操作系统自身/var/log/journal、/var/cache、旧内核缓慢增长容易长期被忽略低但需要知道哪些能删镜像与运行时层是大头。一个 500MB 的镜像在节点上被拉取十次不同 tag占用的可能远不止 5GB因为镜像层是复用的但不同 tag 指向不同 manifest 时复用率会下降。很多团队 CI 里给镜像打的是latest加时间戳每次发布都是新 tag节点上的旧 tag 镜像如果没被 GC 掉就会一直躺着。容器日志是第二大来源也是最容易失控的。kubelet 默认的containerLogMaxSize是 10MicontainerLogMaxFiles是 5理论上单容器日志上限 50Mi。但很多集群在初始化时把这两个值调大了或者干脆没配某些运行时版本下默认行为又不一样结果就是单个容器写到几个 GB。可写层与临时卷比较隐蔽。emptyDir卷在 Pod 删除后会被清理但如果 Pod 卡在Terminating状态或者 kubelet 与 API Server 通信异常导致 Pod 元数据残留目录就会留在/var/lib/kubelet/pods下面。另外容器内往非挂载路径写文件写的是可写层这部分空间在容器删除前一直占着。操作系统自身的占用经常被低估。systemd journal 默认可以涨到宿主磁盘的 10%/var/cache/apt或dnf的包缓存、旧内核、core dump 文件这些加起来几个 GB 很常见。2. 清理前的定位别急着 rm -rf磁盘清理最大的风险不是清不干净而是清错了。生产节点上误删一个正在被进程打开的文件可能引发的问题比磁盘满本身更严重。所以清理前的定位环节投入的时间应该占到整个处理流程的一半以上。2.1 三层排查法大盘、目录、文件句柄我习惯把定位拆成三层从粗到细每层都有明确的判据。第一层看大盘水位和 inode。df -h看容量df -i看 inode。有些节点容量还有 30%但 inode 用光了表现同样是写不进去。日志类场景里小文件极多inode 耗尽是真实存在的。df -h df -i如果发现某个挂载点容量和 inode 都很宽松但 kubelet 仍然报 DiskPressure那要怀疑 kubelet 监控的imagefs和nodefs不是同一个文件系统报的是另一个分区的问题。第二层看目录分布。用du从根往下逐层收窄注意加-x参数避免跨文件系统统计否则会跑到挂载的 PV 里去既慢又不准。du -x -h --max-depth1 / 2/dev/null | sort -hr | head -20 du -x -h --max-depth1 /var/lib 2/dev/null | sort -hr | head -20 du -x -h --max-depth2 /var/log 2/dev/null | sort -hr | head -20这一轮基本能锁定是/var/lib/containerd还是/var/log/pods出了问题。如果节点上装了ncdu交互式浏览会舒服很多但生产环境不一定允许临时装包du够用。第三层看文件句柄。这一步是很多人会漏掉的。如果du统计出来的总量和df报的已用空间差了好几 GB说明有文件被删除但句柄没释放——进程还开着这个文件空间就不会归还。lsof L1 2/dev/null | head -30L1表示列出 link count 小于 1 的文件也就是已被删除但仍被打开的文件。输出里重点看 SIZE 列大的那几行对应的进程通常是日志采集组件、容器运行时或者某个还在写日志的应用。2.2 用运行时命令把镜像和容器的账算清楚du只能告诉你目录多大说不清哪些能删。要精确判断镜像和容器的占用得用容器运行时自己的命令。现在主流集群用的是 containerd对应的 CLI 是crictl老一些的集群可能还是 Docker命令换成docker即可逻辑一样。先看镜像总量和各个镜像的大小crictl images crictl imagefsinfoimagefsinfo会返回imageFilesystem的usedBytes和inodesUsed这个数值和 kubelet 看到的是一致的可以拿来和df对照确认 kubelet 监控的确实是这个分区。再看容器重点区分运行中和已退出的crictl ps -a crictl ps -a --state exited已退出的容器如果数量很多它们的可写层都是垃圾。正常情况下 kubelet 会按--maximum-dead-containers之类的策略回收但如果节点上 Pod 频繁重启、或者 kubelet 的 GC 没跑起来残留会很多。还有一个容易被忽略的点镜像的引用计数。同一个镜像 ID 可能被多个 tag 引用crictl rmi删一个 tag 不一定释放空间。判断某个镜像能不能安全删掉要看它是否还被某个容器包括已退出的或者 Pod sandbox 引用着。crictl inspecti image-id | grep -A5 repoTags真正安全的批量清理是只删没有被任何容器引用的镜像crictl rmi --prune做的就是这件事后面会展开。3. 分类清理实操按占用来源逐个击破定位清楚之后清理动作要按来源分类做顺序上建议先清日志和运行时垃圾低风险、见效快再处理卷和系统层需要确认。每做一步用df -h复查一次避免多个动作叠加导致误判。3.1 运行时层无用镜像、停止容器与沙箱残留镜像清理分三档风险从低到高。第一档清理悬空镜像。悬空镜像指的是没有 tag 指向的镜像层通常是重新构建同名 tag 后留下的旧层。这类镜像没有任何引用价值直接清。# containerd crictl rmi --prune # docker docker image prune -f第二档清理未被容器引用的镜像。这一步会删掉所有当前没有容器在用的镜像包括某些你打算回滚用的旧版本镜像。执行前确认一下回滚窗口别在发布刚完成时跑。crictl images | awk NR1 $2none {print $3} | xargs -r crictl rmi如果集群用的是自定义脚本管理镜像更稳的做法是先导出引用清单再对比# 导出所有容器正在使用的镜像 ID crictl ps -a -q | xargs -r crictl inspect --output go-template \ --template {{.status.imageRef}}{{\n}} 2/dev/null | sort -u /tmp/in-use-images.txt # 列出所有镜像人工比对后删除不在清单里的 crictl images --no-trunc第三档清理已退出的容器和沙箱。已退出容器的可写层和日志都会占空间。kubelet 通常在 Pod 删除时一起清理但异常情况下会有残留。# 删除所有已退出的容器 crictl ps -a --state exited -q | xargs -r crictl rm # 清理无主的 Pod sandbox crictl pods --state notready -q | xargs -r crictl rmp注意crictl rmp删的是 sandbox如果对应的 Pod 其实还在运行只是状态上报有问题删掉会导致容器异常。执行前用kubectl get pod -o wide交叉核对节点上的 Pod 列表。执行完之后再跑一次crictl imagefsinfo看usedBytes是否下降。如果降幅远小于预期很可能就是 2.1 节说的文件被删但句柄没释放这时候要去找持有句柄的进程而不是继续删。3.2 容器日志truncate 而不是 rm容器日志的路径结构是这样的/var/log/pods/namespace_pod_uid/container/restart.log/var/log/containers下面是指向这些文件的软链接。日志采集组件一般从/var/log/containers读。清理日志的核心原则是能截断就不删除能按文件截断就不批量删目录。原因有两个一是删除文件会让采集组件的读取偏移失效二是rm之后空间不一定立刻释放句柄还在。先找出大文件# 找出超过 200MB 的容器日志 find /var/log/pods -type f -name *.log -size 200M -exec ls -lh {} \; 2/dev/null # 按大小排序看前 20 du -ah /var/log/pods 2/dev/null | sort -hr | head -20然后截断# 单个文件截断保留 inode采集组件不受影响 truncate -s 0 /var/log/pods/ns_pod_uid/container/0.log # 批量截断所有超过 200MB 的日志 find /var/log/pods -type f -name *.log -size 200M -exec truncate -s 0 {} \;truncate -s 0保留文件本身只把内容清空采集组件的 tail 逻辑会看到文件被截断多数实现能正确处理。相比之下rm会让采集组件的文件句柄指向一个已删除的 inode表现为日志采集卡住。如果确认要删比如 Pod 已经不存在了只是目录残留那就先确认没有采集组件在跟踪# 查一下有没有进程打开这些文件 lsof /var/log/pods/残留目录 2/dev/null没有输出再删。日志清理只能解决当下要让它不再涨得回到 kubelet 的日志轮转配置这部分放到第 4 节。3.3 卷与临时目录EmptyDir、残留 Pod 目录与镜像构建缓存/var/lib/kubelet/pods下面每个 Pod 一个目录包含 volumes、containers、plugins 等子目录。正常删除的 Pod 目录会被 kubelet 清掉但如果 kubelet 在处理删除时被打断比如节点重启、kubelet 重启、API Server 不可达就会留下孤儿目录。判断孤儿目录的方法是和 API Server 上的 Pod UID 做对比# 节点上实际存在的 Pod 目录 ls /var/lib/kubelet/pods /tmp/node-pod-uids.txt # API Server 上该节点所有 Pod 的 UID含 Terminating kubectl get pods --all-namespaces --field-selector spec.nodeNamenode-name \ -o jsonpath{range .items[*]}{.metadata.uid}{\n}{end} /tmp/api-pod-uids.txt # 差集就是孤儿目录 comm -23 (sort /tmp/node-pod-uids.txt) (sort /tmp/api-pod-uids.txt)差集里的目录先确认对应 Pod 确实不存在了包括 DaemonSet 重建的场景再删除comm -23 (sort /tmp/node-pod-uids.txt) (sort /tmp/api-pod-uids.txt) \ | xargs -r -I{} rm -rf /var/lib/kubelet/pods/{}注意删除顺序建议先停 kubelet 或者至少确认 Pod 已经不在 API Server 里否则可能出现 kubelet 正在写入而你在删除的竞争情况。生产环境稳妥的做法是systemctl stop kubelet清完再start代价是节点短暂不可调度需要提前 cordon。emptyDir的清理依赖 Pod 生命周期这个没法手动干预但有一个配置项值得关注emptyDir.sizeLimit。设置了之后kubelet 会在卷超过限制时驱逐 Pod等于给临时卷加了一道闸。没设置的话一个写疯了的应用理论上能把整个节点写满。volumes: - name: cache emptyDir: sizeLimit: 5Gi另外节点上如果有构建类负载比如跑 CI Runner 的节点/var/lib/docker如果还在用 Docker或者 buildkit 的缓存目录也要看一眼。构建缓存的特点是单个文件不大但数量极多清理时用docker builder prune或buildctl prune更安全。3.4 操作系统层journal 日志、包缓存与旧内核系统层清理相对独立不涉及 Kubernetes 对象但一样要小心。systemd journal是最常见的隐形大户。默认配置下 journal 可以占用/var/log/journal所在文件系统的 10%在 100GB 的盘上就是 10GB。journalctl --disk-usage journalctl --vacuum-size500M journalctl --vacuum-time7d--vacuum-size和--vacuum-time二选一即可同时用取更严格的那个。想永久改的话编辑/etc/systemd/journald.conf设置SystemMaxUse500M和MaxRetentionSec7day然后systemctl restart systemd-journald。包管理缓存Debian 系apt-get cleanRHEL 系dnf clean all。这些目录通常在/var/cache下几百 MB 到几 GB 不等清了没有副作用。旧内核只保留当前运行版本和上一个版本其余的可以删。RHEL 系用package-cleanup --oldkernels --count2Debian 系用apt-get autoremove --purge。这条要谨慎删错了内核会导致节点重启后起不来。core dump 和其他杂项/var/crash、/var/lib/systemd/coredump、/tmp下的旧文件都可以顺手看一眼。/tmp特别容易被遗忘有些应用会把临时文件写到那儿且不清理。du -xh --max-depth1 /var 2/dev/null | sort -hr | head -15 find /tmp -type f -mtime 7 -size 100M -exec ls -lh {} \; 2/dev/null4. 从救火到预防让磁盘不再月月告警手工清理解决的是这次要让它不复发得从 kubelet 的回收机制、日志轮转策略、以及节点巡检三个方向同时下手。这三件事做完节点磁盘告警的频率通常能从每周降到每季度甚至更低。4.1 imageGC 与驱逐阈值四个参数决定回收行为kubelet 有两套独立的回收机制很多人会搞混。镜像回收ImageGC由两个阈值控制imageGCHighThresholdPercent和imageGCLowThresholdPercent。磁盘使用率超过高水位时触发回收一直删到低于低水位为止。默认值是 85 和 80但这两个默认值在现代大容量磁盘上偏激进——一个 1TB 的盘85% 意味着要涨到 850GB 才开始回收往往等不到那时候就被驱逐了。驱逐Eviction由evictionHard、evictionSoft等参数控制是直接驱逐 Pod比镜像回收更重。两者的关系是镜像回收先跑把水位降下来如果降不下来驱逐兜底。一个比较稳妥的配置组合写成 kubelet 的配置文件apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration imageGCHighThresholdPercent: 75 imageGCLowThresholdPercent: 65 evictionHard: nodefs.available: 10% nodefs.inodesFree: 5% imagefs.available: 15% memory.available: 500Mi evictionSoft: nodefs.available: 15% imagefs.available: 20% evictionSoftGracePeriod: nodefs.available: 1m30s imagefs.available: 1m30s evictionMinimumReclaim: nodefs.available: 2Gi imagefs.available: 2Gi几个参数的选择逻辑值得解释一下。imageGCHighThresholdPercent设成 75比默认的 85 提前 10 个百分点。这样在磁盘还没到危险区的时候就开始回收镜像回收动作对业务无感——只是删掉无人使用的镜像层。低水位设成 65留出 10 个百分点的回收区间避免删一点就停、停一下就涨回去的抖动。evictionHard是硬阈值达到就立刻驱逐没有宽限期。nodefs.available设 10%imagefs.available设 15%后者高一些是因为镜像层增长通常更陡。nodefs.inodesFree单独设 5% 是防 inode 耗尽这个指标在日志密集的节点上很容易先于容量告警。evictionSoft配GracePeriod是软驱逐给 Pod 一段缓冲时间自己退出比硬驱逐温和得多。evictionMinimumReclaim的作用是防止每次只回收一点点就停导致频繁触发。设成 2Gi 表示一次至少回收 2Gi减少抖动。注意imagefs和nodefs在某些部署下是同一个文件系统比如只有一个数据盘的节点这时候两个阈值会互相影响建议先确认节点上docker info或crictl info报的根目录和/var/lib/kubelet是不是同一分区。4.2 日志轮转kubelet 与采集端要配对kubelet 的containerLogMaxSize和containerLogMaxFiles决定了单容器日志的硬上限。默认 10Mi 和 5也就是 50Mi 封顶实际场景里这个值偏小容易被应用日志刷爆导致丢日志很多团队会调大到 100Mi x 5 或者 200Mi x 3。containerLogMaxSize: 100Mi containerLogMaxFiles: 5这两个参数在 kubelet 配置文件里生效改完要systemctl restart kubelet。重启 kubelet 不会重启业务容器但会让节点短暂不可调度建议在维护窗口做或者先cordon。轮转生效之后日志总占用就变成可预测的Pod 数量 × 申明周期内的容器数 × 100Mi × 5。一个节点上跑 50 个 Pod、每个 Pod 1 个容器理论上限就是 25GB实际远低于此。但这里有个坑日志采集端的配置必须和轮转策略对齐。如果采集组件是按 inode 跟踪文件的比如某些 filebeat 配置轮转产生的新文件可能不被跟踪导致日志丢失。稳妥的做法是让采集端按文件名通配 记录读取位置并且显式配置多文件读取。另一个坑是应用自身的日志写入方式。如果应用往容器内的文件写日志而不是输出到 stdout/stderr那 kubelet 的轮转完全不生效这部分日志写在可写层里既占空间又不受控。这类应用要改造成输出到标准输出或者在容器内配置 logrotate。4.3 一套可落地的巡检脚本与自愈思路策略配好之后还需要一个兜底的巡检把某天某个节点突然飙高的情况尽早发现。思路是每天定时跑一次巡检输出每个节点的磁盘水位、镜像占用、大日志文件清单超过阈值就告警。巡检脚本的核心逻辑#!/bin/bash # 节点磁盘巡检输出 JSON 供采集 THRESHOLD80 ROOT_USAGE$(df -h / | awk NR2 {gsub(/%/,,$5); print $5}) IMAGE_USAGE$(crictl imagefsinfo 2/dev/null | \ awk /usedBytes/{gsub(/[^0-9]/,,$0); print $0; exit}) BIG_LOGS$(find /var/log/pods -type f -name *.log -size 100M 2/dev/null | wc -l) echo {\node\:\$(hostname)\,\root_usage_pct\:${ROOT_USAGE},\image_bytes\:${IMAGE_USAGE:-0},\big_log_count\:${BIG_LOGS}} if [ ${ROOT_USAGE} -ge ${THRESHOLD} ]; then # 触发告警可对接你们现有的告警通道 echo ALERT: root usage ${ROOT_USAGE}% on $(hostname) fi这个脚本用 DaemonSet 挂 hostPath 跑或者用 node-exporter 的 textfile collector 上报。前者的好处是不依赖额外组件后者可以直接复用现有的监控栈。自愈的部分要谨慎。我见过有团队写了磁盘超过 90% 自动跑crictl rmi --prune的 CronJob短期看有效但长期跑下来会把一些本来该保留的镜像删掉导致 Pod 重启时拉不到镜像。自动清理只建议做三件事截断超过阈值的日志、清理悬空镜像--prune、清理悬空卷。其他的都留给人工判断。还有一个成本低但效果好的做法给不同业务类型的节点打标签用不同的 kubelet 配置。离线计算节点和在线服务节点的磁盘特征完全不同前者镜像多、Pod 短命后者日志多、Pod 长寿用同一套阈值必然有一边不合适。5. 踩过的坑与排查速查表清理这件事做十次可能九次顺利剩下那一次就会让你记很久。下面这些是从实际故障里总结出来的每条都对应真实发生过的场景。5.1 磁盘删了不释放的三种真相第一种文件被进程持有。前面提过rm之后 inode 还被进程占着df看不到变化。用lsof L1定位找到进程后重启它。常见持有者是日志采集组件、被删除但没退出的容器进程、以及某些不规范的 agent。第二种容器可写层没有随容器删除而释放。有些运行时版本在容器删除后可写层的目录还在需要手动清理或者等运行时 GC。表现是crictl ps -a里已经看不到这个容器了但du统计/var/lib/containerd还是很大。解决办法是重启容器运行时systemctl restart containerd代价是节点上的容器会全部重建。第三种文件系统层面的碎片或者保留块。ext4 默认给 root 保留 5% 的空间df报的 available 已经扣掉了这部分。tune2fs -m 1 /dev/sdX可以把保留比例降到 1%在数据盘上通常安全但系统盘不建议改否则 root 出问题时可能连日志都写不进去。这三种情况的现象很像都是明明删了但空间没回来区分的关键就是lsof L1有没有输出、du和df的差值有多大。5.2 常见问题速查表现象可能原因快速验证处理方式df -h显示空间已释放du统计仍很大目录被挂载覆盖mount | grep dir卸载后重新统计清理后空间不降文件被进程持有lsof L1重启持有进程Pod 频繁被驱逐但磁盘还有空间kubelet 监控的 imagefs 与 nodefs 不是同一分区crictl imagefsinfo对比df调整阈值或分区规划日志采集出现断档用rm删了正在采集的日志检查采集组件日志改用truncate采集端重配inode 耗尽但容量正常小文件过多日志、缓存df -i清理小文件调整轮转节点重启后容器全部消失清理时误删了/var/lib/kubelet下活跃目录对比 API Server Pod 列表按 UID 差异精确清理不要整目录删镜像 GC 后 Pod 拉不到镜像镜像被其他节点依赖本地 GC 误删kubectl describe pod看拉取错误配置私有仓库或保留关键 tag避坑清单五条都是我实际踩过的不要整目录rm -rf /var/lib/kubelet/pods/*。节点上正在运行的 Pod 目录混在里面删了会导致容器异常表现是容器还在但 kubelet 找不到它最终触发重建。必须按 UID 差异精确删除。不要在 Kubelet 运行中删除/var/lib/containerd下的内容。运行时会持有大量文件句柄强删会导致运行时状态不一致。要么用运行时命令要么停服务再清。不要用find / -name *.log -delete这种命令。会命中的路径远超你想象包括应用自己管理的日志、数据库的 WAL 文件。范围一定要限定在/var/log/pods和/var/log/containers下面。清理前先cordon节点并确认没有新的 Pod 调度过来。尤其是 DaemonSet 类的 Pod删了目录之后可能被重建到同一节点边清边写会让统计失真。每次清理动作都要留记录。不只是我删了什么还要记删之前的水位、删之后的水位、用了哪条命令。下次再出问题时这份记录能帮你快速判断是该清哪一类而不是从头再排查一遍。最后分享一个我觉得最有用的习惯把节点的磁盘趋势图拉出来看斜率而不是只看绝对值。70% 但一天涨 5% 的节点比 85% 但一周涨 1% 的节点危险得多。前者说明有一个明确的增长源在持续写后者可能只是一次性的大镜像没清。盯着斜率做容量规划比盯着阈值做救火要省心得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询