嘿,各位小伙伴们!你们有没有遇到过这样的灵魂拷问:
- “我服务器这个月的流量费怎么又超了?到底是谁干的!” 😱
- “我跑了好几个 Docker 应用,哪个才是真正的‘流量刺客’?”
- “我想给客户按容器使用流量计费,该怎么算才准?”
别担心!今天,我将带你踏上一场有趣的侦探之旅,我们将使用 Linux 系统自带的神器 iptables
,来精确地揪出每一个 Docker 容器的流量小秘密,让它们在你面前“一丝不挂”!而且,完全免费!
第一站:最简单的场景 (端口号一致) 😊
假设我们有一个 Web 应用,启动命令是这样的:
docker run -d --name my-web-app -p 38022:38022 my-image
这里,宿主机的 38022
端口映射到了容器内部的 38022
端口。这是最简单、最直接的情况。
我们的侦探工具:iptables
!你可以把它想象成是服务器的网络守门人,所有进出的数据包都得经过它的盘问。
我们的侦查地点:Docker 很贴心地为我们准备了一个绝佳的“蹲点”位置,叫做 DOCKER-USER
链。所有准备进入 Docker 容器的流量,都会经过这里。我们只要在这设下关卡,就能轻松记账!
记账咒语 (执行命令):
我们需要设置两条规则,一条记录“进来”的流量,一条记录“出去”的流量。
# 规则1: 记录所有“进入”容器的流量 (目标端口是 38022)sudo iptables -I DOCKER-USER -p tcp --dport 38022
# 规则2: 记录所有从容器“出来”的流量 (源端口是 38022)sudo iptables -I DOCKER-USER -p tcp --sport 38022
--dport
(Destination Port): 就像是收件地址上的门牌号。所有寄往38022
端口的“包裹”(数据包),都会被这条规则记下一笔。这就是入站流量 (Inbound)。📬--sport
(Source Port): 就像是寄件人地址。所有从38022
端口寄出的“包裹”,也会被记下一笔。这就是出站流量 (Outbound)。📤
查看账本:
随时可以运行下面的命令来查看最新的流量统计:
sudo iptables -vnL DOCKER-USER
你会看到类似这样的输出:
Chain DOCKER-USER (1 references) pkts bytes target prot opt in out source destination 30 10204 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:38022 17 5791 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:38022
dpt:38022
那一行的bytes 10204
就是你的入站总流量。spt:38022
那一行的bytes 5791
就是你的出站总流量。pkts
记录的是数据包的数量。
看到 bytes
在不断增加,恭喜你,你已经成功了!🎉
第二站:剧情反转 - 端口号不一致 🤔
在真实世界里,我们经常会把容器内部的 80
端口映射到宿主机的一个随机端口,比如 38022
。
docker run -d --name another-app -p 38022:80 nginx
问题来了:我们应该监听宿主机的 38022
端口,还是容器的 80
端口呢?
揭晓谜底:答案是,永远只关心容器内部的端口! 🎯
为什么呢?这里有个小故事:
- 一个外部请求(包裹)寄往你服务器的
38022
端口。 - 门口的大爷 Docker(门卫)收到了包裹。他查了一下登记表,发现这个是要给“住在 80 号房间的 Nginx 先生”的。
- 于是,门卫大爷把包裹上的地址从
38022
撕掉,贴上了一个新地址80
,然后把包裹放进了内部电梯(也就是DOCKER-USER
链)。 - 我们的
iptables
侦探正是在电梯口检查包裹。当他看到这个包裹时,上面的地址已经是80
了!
所以,正确的咒语应该是:
# 监听进入容器的流量,它的目标端口已经是 80 了sudo iptables -I DOCKER-USER -p tcp --dport 80
# 监听从容器出来的流量,它的源端口自然也是 80sudo iptables -I DOCKER-USER -p tcp --sport 80
记住这个黄金法则:忽略宿主机端口,紧盯容器端口!
第三站:高难度挑战 - 多个容器,相同内网端口 🤯
如果我们有两个应用,它们的容器内部端口都是 80
呢?
- WebApp 1:
docker run --name webapp1 -d -p 8080:80 nginx
- WebApp 2:
docker run --name webapp2 -d -p 8081:80 nginx
如果还只用 --dport 80
来统计,那两个应用的流量就会混在一起,像一碗浆糊。这可不行!
我们的超级武器:容器的内部 IP 地址! 🔫
每个容器在 Docker 的世界里都有一个独一无二的身份证号——它的内部 IP。我们只要在记账时,不仅看端口号,还核对一下 IP 地址,就能完美区分它们了!
第一步:获取它们的 IP 地址
# 获取 webapp1 的 IPdocker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webapp1# ==> /webapp1 - 172.17.0.2
# 获取 webapp2 的 IPdocker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webapp2# ==> /webapp2 - 172.17.0.3
(你的 IP 地址可能不同,以命令输出为准)
第二步:念出终极咒语(指定 IP 和端口)
现在我们的咒语升级了,加入了 -d
(目标 IP) 和 -s
(源 IP) 参数。
为 WebApp 1 (172.17.0.2
) 记账:
sudo iptables -I DOCKER-USER -p tcp -d 172.17.0.2 --dport 80 # 入站sudo iptables -I DOCKER-USER -p tcp -s 172.17.0.2 --sport 80 # 出站
为 WebApp 2 (172.17.0.3
) 记账:
sudo iptables -I DOCKER-USER -p tcp -d 172.17.0.3 --dport 80 # 入站sudo iptables -I DOCKER-USER -p tcp -s 172.17.0.3 --sport 80 # 出站
现在再去看账本 sudo iptables -vnL DOCKER-USER
,你就会看到四条独立的、清清楚楚的记录,每一条都对应着特定容器的特定方向的流量。完美!🏆
第四站:生产环境终极秘籍 - 稳定可靠的秘诀 🚀
问题:如果容器因为更新或其他原因被删除重建,它的内部 IP 可能会改变,这会导致我们辛辛苦苦设置的 iptables
规则失效!这在生产环境是绝对不能接受的。
解决方案:使用 Docker Compose 和自定义网络来为容器分配静态内部 IP 地址。这是在生产环境中管理多容器应用的推荐做法,一劳永逸!
下面是一个 docker-compose.yml
的例子,它就像是容器们的“出生证明”和“户口本”:
version: '3.8'
services: webapp1: image: nginx ports: - "8080:80" networks: my_app_net: ipv4_address: 172.20.0.10 # 分配一个固定的 IP,就像门牌号
webapp2: image: nginx ports: - "8081:80" networks: my_app_net: ipv4_address: 172.20.0.11 # 分配另一个固定的 IP
networks: my_app_net: driver: bridge ipam: config: - subnet: 172.20.0.0/24
通过这种方式,webapp1
的 IP 将永远是 172.20.0.10
,webapp2
的 IP 永远是 172.20.0.11
,无论它们被重启多少次。你的 iptables
规则也将因此而变得坚如磐石,稳定可靠!
重要!别忘了保存规则!⚠️
iptables
的规则在服务器重启后会消失。为了让我们辛苦设置的规则能够“开机自启”,需要把它们保存下来。
-
安装持久化工具:
Terminal window # Debian/Ubuntusudo apt-get install iptables-persistent# CentOS/RHELsudo dnf install iptables-services -
保存规则 (就像游戏存档!):
Terminal window # Debian/Ubuntusudo netfilter-persistent save# CentOS/RHELsudo service iptables save
另外,如果你想 清空计数器,重新开始统计,只需运行:
sudo iptables -Z DOCKER-USER
侦探收工:抹掉所有痕迹!🕵️♀️💨
好了,我们的监控任务圆满完成了,或者你只是想清理一下不再需要的规则。作为一名专业的侦探,撤离时必须不留下一丝痕迹。删除 iptables
规则就像擦掉黑板上的字一样简单,主要有两种方法:
方法一:精准狙击(按规则内容删除)
如果你还记得当初添加规则时念的“咒语”,最直接的方法就是把插入 (-I
) 或追加 (-A
) 命令换成删除 (-D
) 命令。
比如说,我们之前为 WebApp 1 (172.17.0.2
) 添加了这条入站规则:
# 这是添加时的命令sudo iptables -I DOCKER-USER -p tcp -d 172.17.0.2 --dport 80
要删除它,只需把 -I
换成 -D
,其他部分一字不差地再执行一遍:
# 这就是删除它的命令sudo iptables -D DOCKER-USER -p tcp -d 172.17.0.2 --dport 80
搞定!这条规则就从规则链中消失了。这个方法非常精确,但缺点是你需要准确地知道整条规则的内容。
方法二:按编号抓人(按行号删除)
当你有很多规则,或者记不清具体内容时,这个方法简直是救星。它就像在队伍里按编号揪人出来,又快又准。
第一步:给所有规则编上号,列出清单
运行 list
命令,并加上一个神奇的参数 --line-numbers
:
sudo iptables -L DOCKER-USER --line-numbers
你会看到一个带有编号的列表:
Chain DOCKER-USER (1 references)num target prot opt source destination1 ... tcp -- anywhere 172.17.0.3 tcp dpt:802 ... tcp -- 172.17.0.3 anywhere tcp spt:803 ... tcp -- anywhere 172.17.0.2 tcp dpt:804 ... tcp -- 172.17.0.2 anywhere tcp spt:80
(这里的 num
就是行号)
第二步:按编号直接删除
假设你想删除第 3 条规则(WebApp 1 的入站规则),直接告诉 iptables
它的编号就行:
sudo iptables -D DOCKER-USER 3
砰!第 3 条规则就被删除了。
🚨 高能预警:一个重要的小陷阱!
当你删除一条规则后,下面规则的编号会自动向上移动!比如,你删除了第 3 条,那么原来第 4 条规则现在就变成了新的第 3 条。
所以,如果你想删除多条规则,一个万无一失的技巧是:从编号大的开始,从下往上删! 这样就不会因为编号变化而出错了。
最后,别忘了更新你的存档! 在你增删改查,对规则满意之后,记得再次运行保存命令,把最新的规则集固化下来,以防服务器重启后又回到了旧状态。
# Debian/Ubuntusudo netfilter-persistent save
# CentOS/RHELsudo service iptables save