Port-Light 可视化端口占用看板:Docker Compose 部署与实战避坑指南
端口占用这件事,说大不大,说小也真能让人抓狂。你可能遇到过这种情况:本地起个服务,控制台甩出一句 Address already in use,然后你打开任务管理器或者 netstat 一顿翻,满屏的 PID 和端口号看得眼睛发花,最后干脆重启电脑了事。更麻烦的是在服务器上,几十个容器跑着,某个端口被谁占了、占它的是哪个服务、这个服务该不该杀,全靠记忆和运气。Port-Light 这个项目就是冲着这个痛点来的——它把端口占用情况做成一块可视化的看板,谁占了哪个端口、进程是什么、容器叫什么名字,一眼就能看明白。这篇内容我会从零开始,把 Port-Light 的部署过程、配置细节、实际使用中的坑和技巧完整讲一遍,不管你是刚接触 Docker Compose 的新手,还是天天跟容器打交道的运维老手,都能直接照着操作。
1. 端口占用排查为什么值得单独做个看板
1.1 传统排查手段的局限到底在哪
大部分人查端口占用,第一反应是 netstat -tulnp 或者 Windows 上的 netstat -ano | findstr :端口号。这招在单机、服务少的时候确实够用,但一旦环境复杂起来,问题就暴露了。netstat 输出的是纯文本,端口、PID、进程名混在一堆,你得自己脑补它们之间的对应关系。更关键的是,它只告诉你"谁占了这个端口",不告诉你"这个进程属于哪个容器、哪个项目、哪个服务"。在 Docker 环境里,宿主机上看到的往往是一个 docker-proxy 或者 containerd-shim 进程占着端口,真正的服务藏在容器里,光看宿主机进程根本对不上号。
还有一个现实问题:排查是一次性的。你今天查完,明天换个人来查,又得从头来一遍。端口占用信息没有被沉淀下来,没有历史记录,没有可视化,每次都是重复劳动。Port-Light 的价值就在于把这些零散的信息聚合起来,做成一块持续更新的看板,让端口占用从"临时排查"变成"随时可查"。
1.2 Port-Light 到底解决了哪些具体场景
我梳理了几个 Port-Light 最能发挥作用的场景。第一个是多容器开发环境,比如你用 Docker Compose 起了一整套服务,MySQL、Redis、Nacos、后端 API 全在一台机器上,端口冲突的概率直线上升。第二个是服务器端口审计,你需要知道哪些端口对外开放、哪些服务在监听、有没有意料之外的服务偷偷占了端口。第三个是故障快速定位,服务起不来报端口冲突,你打开 Port-Light 看板,直接定位到占用者,不用再敲命令。
第四个场景可能很多人没想到:端口规划。在新项目上线前,你需要知道当前机器上哪些端口已经被占用,避免规划了一个已经被占的端口。Port-Light 的看板可以当作端口资源清单来用,这个用途在实际工作中非常实用。
1.3 为什么选择 Docker Compose 部署
Port-Light 本身是一个轻量的服务,用 Docker Compose 部署是最省心的方式。原因有几个:一是依赖隔离,Port-Light 可能需要读取宿主机的网络信息,用容器部署可以精确控制它能看到什么、能访问什么;二是启动简单,一条 docker compose up -d 就搞定,不用手动装 Python、配环境变量、处理依赖冲突;三是便于迁移,换台机器把 compose 文件拷过去就能跑。
不过这里有个关键点要注意:Port-Light 要读取宿主机的端口信息,容器默认是隔离的网络命名空间,直接跑在容器里是看不到宿主机端口的。所以部署的时候必须处理网络模式的问题,这个后面会详细讲。很多人第一次部署 Port-Light 失败,就是卡在这一步。
2. 部署前的环境准备与依赖检查
2.1 Docker 与 Docker Compose 的版本要求
Port-Light 对 Docker 版本有一定要求,建议 Docker 20.10 以上,Docker Compose v2 以上。为什么要卡这个版本?因为 Port-Light 需要用到 host 网络模式下的端口读取能力,以及 Compose v2 的 profiles 特性来做可选配置。如果你用的是老版本的 docker-compose(注意是带横杠的 v1),有些语法会不兼容。
检查版本很简单:
如果 docker compose version 报 unknown command: docker compose,说明你装的是老版本的独立 docker-compose,或者 Compose 插件没装。这种情况在麒麟 V10、部分国产 Linux 发行版上比较常见。解决办法是安装 Compose 插件,或者直接用 docker-compose(带横杠)命令,但要注意语法差异。
Windows 用户如果用的是 Docker Desktop,Compose 是自带的,一般不会有问题。但 Docker Desktop 启动失败的情况也不少,常见报错是 virtualization support not detected,这是 BIOS 里虚拟化没开,或者和 Hyper-V、WSL2 冲突了。这个不是 Port-Light 的问题,但会直接导致你没法部署,所以先确认 Docker Desktop 能正常启动。
2.2 宿主机网络信息的读取权限
这是 Port-Light 部署里最容易踩坑的地方。Port-Light 要读取宿主机的端口占用情况,本质上是要访问宿主机的 /proc/net/tcp、/proc/net/udp 这些文件,或者调用 ss、netstat 命令。在容器里,这些信息默认是看不到宿主机的,只能看到容器自己的网络命名空间。
解决方案有两种。第一种是让 Port-Light 容器使用 network_mode: host,这样容器直接共享宿主机的网络命名空间,读到的就是宿主机的端口信息。第二种是挂载宿主机的 /proc 目录到容器里,但这种方式在部分系统上权限受限,不如 host 模式干净。
我实测下来,network_mode: host 是最稳的方案。但要注意,用了 host 模式之后,Port-Light 自己的端口就直接暴露在宿主机上了,不能再做端口映射。所以 Port-Light 的监听端口要提前规划好,别和已有服务冲突。
2.3 端口规划与冲突预检
在部署之前,先确认 Port-Light 要用的端口没被占用。Port-Light 默认用 8080 或者 3000 这类常见端口,但这些端口太容易被占了。我建议给它分配一个不常用的端口,比如 18080 或者 28080。
预检命令:
如果输出为空,说明端口可用。如果有输出,换个端口。这一步看着简单,但能省掉后面很多麻烦。我见过太多人部署完了发现服务起不来,一查是端口冲突,然后又回头改配置重新部署,白白浪费时间。
另外提醒一句,如果你机器上跑了 MySQL、Redis、Nacos 这些常见服务,它们的默认端口(3306、6379、8848)大概率已经被占了,规划 Port-Light 端口的时候避开这些。
3. Port-Light 的 Compose 编排文件拆解
3.1 完整的 compose 配置与逐行说明
下面是我实际使用的 compose 配置,基于官方配置做了适配和注释:
逐行拆解一下。network_mode: host 让容器共享宿主机网络,这是读取宿主机端口的前提。volumes 里挂载了 /proc 到 /host/proc,只读模式,让 Port-Light 能读到宿主机的网络连接信息。挂载 docker.sock 是为了让 Port-Light 能查询 Docker 容器信息,把端口占用和容器对应起来——这是 Port-Light 相比 netstat 最大的优势。
pid: host 这个配置很多人会忽略,但它很关键。共享 PID 命名空间之后,Port-Light 才能看到宿主机的进程信息,把端口和进程对应上。没有这个配置,你只能看到端口被占,但看不到是哪个进程占的。
环境变量里,LISTEN_PORT 是 Port-Light 自己的监听端口,REFRESH_INTERVAL 是看板刷新间隔,单位是秒。5 秒是个比较平衡的值,太短了频繁刷新浪费资源,太长了信息不及时。
3.2 为什么这些挂载和权限是必须的
有人可能会问,挂载 /proc 和 docker.sock 是不是有安全风险。确实有,docker.sock 挂载进容器意味着容器有权限操作 Docker,这相当于给了容器很高的权限。但在 Port-Light 这个场景下,这是必要的代价,因为它要读取容器信息。如果你对安全要求极高,可以考虑用 Docker API 的只读代理,或者限制 Port-Light 的网络访问范围。
/proc 挂载为只读(:ro)是必须的,Port-Light 只需要读,不需要写。这个细节体现了最小权限原则,能只读就只读。
还有一个容易忽略的点:/proc 挂载在某些系统上可能不完整,比如在 WSL2 里,/proc/net/tcp 的内容和原生 Linux 有差异。如果你在 WSL2 里部署 Port-Light,发现端口信息不全,大概率是这个原因。解决办法是直接在原生 Linux 或者虚拟机里部署,别在 WSL2 里折腾。
3.3 环境变量的调优与常见配置组合
Port-Light 的环境变量不多,但每个都有讲究。除了上面提到的 LISTEN_PORT 和 REFRESH_INTERVAL,还有几个值得关注的:
| 环境变量 | 默认值 | 作用 | 调优建议 |
|---|---|---|---|
| LISTEN_PORT | 8080 | 看板监听端口 | 改成不常用端口,避开冲突 |
| REFRESH_INTERVAL | 10 | 刷新间隔(秒) | 开发机设 5,服务器设 30 |
| TZ | UTC | 时区 | 设成 Asia/Shanghai |
| HOST_PROC | /proc | 宿主机 proc 路径 | host 模式下保持默认 |
| LOG_LEVEL | info | 日志级别 | 排查问题时设 debug |
REFRESH_INTERVAL 的调整要根据使用场景来。如果你是在开发机上用,希望端口变化能快速反映,设 5 秒比较合适。如果是在生产服务器上,端口变化不频繁,设 30 秒甚至 60 秒都行,减少资源消耗。我一般开发机设 5,服务器设 30。
LOG_LEVEL 设成 debug 能看到 Port-Light 读取端口信息的详细过程,排查"为什么某个端口没显示"这类问题时很有用。但平时别开 debug,日志量会很大。
4. 从启动到验证:部署实操全流程
4.1 启动命令与首次启动的观察要点
配置写好之后,启动就一条命令:
但别急着关终端,第一次启动要观察日志:
重点看几件事:有没有报权限错误、有没有成功读取到宿主机端口、监听端口有没有正常绑定。如果日志里出现 permission denied 读取 /host/proc,说明挂载权限有问题;如果出现 address already in use,说明监听端口冲突了。
启动成功后,浏览器打开 http://宿主机IP:18080,应该能看到看板界面。如果打不开,先确认防火墙有没有放行这个端口,再确认 Port-Light 容器是不是真的在运行。
4.2 看板数据的验证方法
看板出来了不代表数据是对的。验证方法很简单:手动起一个占端口的服务,看看看板能不能识别出来。比如:
然后刷新看板,应该能看到 9999 端口被占用,进程是 python3。再把这个服务停掉,看板上 9999 端口应该消失。这个验证过程能确认 Port-Light 的数据读取和刷新都是正常的。
如果你还跑了 Docker 容器,看板上应该能同时看到宿主机进程和容器进程的端口占用,并且容器那部分会显示容器名称。这是 Port-Light 的核心能力,一定要验证到。
4.3 常见启动失败的原因与排查路径
启动失败的情况我遇到过几种,按出现频率排一下:
第一种是端口冲突,占 40% 左右。报错信息是 bind: address already in use。解决办法就是换端口,前面已经讲过预检方法。
第二种是权限问题,占 30%。报错是读取 /host/proc 失败。这种情况通常是 SELinux 或者 AppArmor 在拦截,需要调整安全策略,或者用 privileged: true(不推荐,权限太大)。
第三种是 Docker Desktop 没启动,占 20%。Windows 和 macOS 用户常见,报错是 Cannot connect to the Docker daemon。解决办法就是先把 Docker Desktop 启动起来。
第四种是镜像拉取失败,占 10%。网络问题或者镜像源问题,换个镜像源或者手动 docker pull 一下。
排查的时候,先看 docker compose logs,再看 docker ps 确认容器状态,最后看宿主机端口占用。这个顺序能覆盖 90% 的问题。
5. 实际使用中的技巧与踩坑记录
5.1 端口占用信息的解读要点
看板上的信息不是越多越好,关键是要会解读。我总结几个要点。第一,LISTEN 状态的端口才是真正在提供服务的,ESTABLISHED 是已建立的连接,TIME_WAIT 是等待关闭的连接,后两者不代表端口被"占用"。很多人看到一堆 TIME_WAIT 就慌了,其实这是正常的 TCP 状态。
第二,0.0.0.0:端口 表示监听所有网卡,127.0.0.1:端口 表示只监听本地。前者对外可访问,后者只能本机访问。这个区别在排查"为什么外部访问不了"时很关键。
第三,Docker 容器的端口占用,宿主机上看到的是 docker-proxy 进程,但看板会把它关联到具体的容器。如果关联不上,检查 docker.sock 挂载是否正确。
5.2 和 netstat、ss 命令的配合使用
Port-Light 是看板,不是命令的替代品。有些场景还是得用 ss 或 netstat。比如你要看某个端口的详细连接状态、要看 TCP 队列情况,这些看板做不了。我的习惯是:日常巡检用看板,快速定位;深入排查用 ss -tulnp,看细节。
两者配合的典型流程是:看板上发现异常端口,记下端口号和 PID,然后用 ss -tulnp | grep 端口号 确认,再用 ps -p PID -o cmd 看进程详情。这个流程比单纯用命令快很多,因为看板帮你做了第一轮筛选。
5.3 长期运行时的资源占用与优化
Port-Light 长期跑着,资源占用其实很低,内存一般几十 MB,CPU 几乎可以忽略。但有几个优化点值得注意。一是 REFRESH_INTERVAL 别设太小,5 秒是下限,再小就没必要了。二是日志要定期清理,docker compose logs 攒多了也占空间,可以配 logrotate 或者用 docker compose logs --tail 限制。
三是如果机器上容器特别多(上百个),Port-Light 读取 Docker 信息会变慢,这时候可以调大刷新间隔,或者关掉容器关联功能,只显示端口和进程。
还有一个我踩过的坑:Port-Light 用 host 网络模式,如果宿主机网络配置变了(比如换了网卡、改了 IP),Port-Light 可能需要重启才能正确读取新网络的信息。这个不是 bug,是 host 模式的特性,重启一下就好。
6. 把 Port-Light 用出花来的几个进阶思路
6.1 多机部署与统一看板
单机看板用久了,自然会想多机统一。Port-Light 本身是单机的,但你可以用反向代理把多台机器的看板聚合到一个入口。比如用 Nginx 做路径转发,/machine-a/ 转到 A 机器的 Port-Light,/machine-b/ 转到 B 机器的。这样你打开一个页面就能看所有机器的端口情况。
这个方案的好处是不用改 Port-Light 本身,纯靠外部代理实现。缺点是每台机器都要部署一个 Port-Light,管理成本上去了。如果机器数量多,可以考虑写个脚本批量部署。
6.2 端口变更的监控与告警
Port-Light 是看板,不主动告警。但你可以基于它的数据做告警。思路是定时抓取看板的数据接口(Port-Light 一般会提供 JSON 格式的数据),和基线对比,发现异常端口就发通知。比如某个平时不开放的端口突然被监听了,这可能是异常情况,值得关注。
实现方式可以用 cron + 脚本,也可以用现成的监控工具。Port-Light 的数据接口路径一般在 /api/ports 这类地址,具体看版本。抓取之后做 diff,有变化就触发告警。
6.3 和现有运维体系的集成
如果你已经有 Zabbix、Prometheus 这类监控体系,可以把 Port-Light 的数据接进去。Port-Light 的数据是结构化的,转成 Prometheus 的 metrics 格式不难。接进去之后,端口占用就变成了监控指标,可以做趋势分析、可以做告警规则,比单独看一个看板更有价值。
集成的时候注意一点:Port-Light 的数据是快照,不是时序数据。要接进 Prometheus,得自己写个 exporter 定时抓取并暴露成 metrics。这个工作量不大,但需要一点 Prometheus 的基础。
6.4 一些我踩过的坑和对应解法
最后分享几个我实际踩过的坑。第一个是 SELinux 导致的权限问题,在 CentOS 和麒麟系统上特别常见,报错是读取 /host/proc 被拒绝。解法是临时 setenforce 0 验证,确认是 SELinux 问题后,再配对应的策略,别直接永久关闭 SELinux。
第二个是 Docker Desktop 在 Windows 上的网络模式问题。Windows 的 Docker Desktop 用的是 WSL2 后端,host 网络模式的行为和 Linux 不完全一样,有时候读不到宿主机的端口。这种情况建议直接在 WSL2 的 Linux 环境里部署,别在 Windows 层面折腾。
第三个是端口号显示不全的问题。有些系统上 /proc/net/tcp 的端口是十六进制显示的,Port-Light 如果没做转换,看板上会显示成十六进制。这个在新版本里已经修了,如果你用的是老版本,升级一下就好。
第四个是容器关联失败。看板上端口显示了,但容器名称是空的。这通常是 docker.sock 挂载路径不对,或者 Docker 版本太老不支持某些 API。检查挂载路径,确认 Docker 版本,基本能解决。
Port-Light 这个项目本身不复杂,但部署和使用中的细节不少。我个人的体会是,把它当成一个"端口资源地图"来用,而不是一个临时排查工具,价值会大很多。日常巡检的时候扫一眼,心里对机器上跑了什么、占了哪些端口有个数,真出问题的时候定位速度会快很多。另外,部署的时候别图省事跳过验证步骤,花五分钟验证数据准确性,能省掉后面几小时的排查时间。