容器网络隔离
面试题
Docker 如何实现容器之间的网络隔离?有哪些隔离机制和配置方式?
标准答案
网络隔离是 Docker 安全性的重要组成部分。Docker 通过 Linux 网络命名空间(Network Namespace)实现基础的隔离,并通过 Docker 网络模型提供更灵活的隔离策略。
隔离层级
第一层:网络命名空间
└── 每个容器拥有独立的网络栈
第二层:网桥隔离
└── 不同网桥或自定义网络间的隔离
第三层:iptables 规则
└── 容器级别的流量控制
第四层:网络策略(需要第三方插件)
└── Kubernetes NetworkPolicy 等
1. 网络命名空间(基础隔离)
每个容器运行时拥有独立的网络命名空间,包括独立的:
# 网络接口
# IP 地址
# 路由表
# iptables 规则
# 端口号空间
docker run -d --name container-a alpine sleep 3600
docker run -d --name container-b alpine sleep 3600
# 两个容器的网络完全隔离
docker exec container-a ip addr
# 只看到 lo 和 eth0(172.17.0.2)
docker exec container-b ip addr
# 只看到 lo 和 eth0(172.17.0.3)
# 端口互不影响
docker run -d -p 80:80 nginx
# 另一个容器也可以在内部监听 80 端口
docker exec -d container-a sh -c "nc -l -p 80"
2. 网桥隔离
默认 bridge 的互通性
# 默认 bridge 网络中的容器默认互通
docker run -d --name app1 alpine sleep 3600
docker run -d --name app2 alpine sleep 3600
docker exec app1 ping 172.17.0.3 # ✅ 能通
自定义 bridge 的隔离性
# 不同自定义网络完全隔离
docker network create net-a --subnet 10.0.1.0/24
docker network create net-b --subnet 10.0.2.0/24
docker run -d --name app-a --network net-a alpine sleep 3600
docker run -d --name app-b --network net-b alpine sleep 3600
docker exec app-a ping 10.0.2.2 # ❌ 不通
同一网络内的容器互通
# 同一自定义网络内的容器默认互通
docker network create app-net
docker run -d --name svc1 --network app-net alpine sleep 3600
docker run -d --name svc2 --network app-net alpine sleep 3600
docker exec svc1 ping svc2 # ✅ DNS 解析自动工作
3. 限制容器间通信
方式一:指定 –icc=false(跨容器通信)
# 在 daemon.json 中设置
# /etc/docker/daemon.json
{
"icc": false
}
systemctl restart docker
# 这样默认 bridge 中的容器不能互相通信
# 但同一个自定义网络中的容器仍然可以通信
方式二:使用 iptables 限制
# 禁止特定容器之间的通信
# 在 DOCKER-USER 链中添加规则
iptables -I DOCKER-USER -i docker0 -o docker0 -j DROP
# 这会阻止默认 bridge 上所有容器间的通信
# 更精细的控制:只允许特定 IP 互通
iptables -I DOCKER-USER -i docker0 -o docker0 \
-s 172.17.0.2 -d 172.17.0.3 -j ACCEPT
iptables -I DOCKER-USER -i docker0 -o docker0 -j DROP
方式三:使用 none 网络
# 最高级别的网络隔离
docker run -d --network none --name isolated alpine sleep 3600
docker exec isolated ip addr
# 1: lo: mtu 65536 qdisc noqueue
# 只有回环接口,没有 eth0
docker exec isolated ping 8.8.8.8
# ping: sendto: Network is unreachable ❌
4. 通过容器名实现”服务可见性”
# 将容器连接到需要被访问的网络
docker network create public-net
docker network create private-net
docker run -d --name api \
--network public-net \
--network private-net \
myapi:latest
docker run -d --name web \
--network public-net \
nginx:alpine
docker run -d --name db \
--network private-net \
postgres:15
# web 能访问 api(共享 public-net)
# db 能被 api 访问(共享 private-net)
# web 不能直接访问 db(不在同一网络)
安全最佳实践
# 1. 微服务分层隔离
# 前端层、业务层、数据层使用不同网络
# 2. 最小权限原则
# 容器只连接它需要的网络
# 3. 定期审计网络
docker network ls
docker network inspect | jq '.[].Containers'
# 4. 删除不需要的网络
docker network prune
# 5. 使用 --icc=false + 自定义网络
实际案例
# docker-compose.yml 隔离实践
version: '3.8'
services:
# 外部可访问
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend
# 内部业务逻辑
api:
image: myapi:latest
networks:
- frontend
- backend
# 数据库,仅内部访问
db:
image: postgres:15
networks:
- backend
networks:
frontend:
backend:
面试问答
问:容器网络隔离的底线是什么?
即使容器之间网络隔离了,它们还是在同一个宿主机上共享内核。所以网络隔离 ≠ 完全隔离,还需要配合用户隔离(rootless)、capability 控制等。
问:怎么验证容器间网络是否隔离?
# 方法一:从容器 A ping 容器 B
docker exec container-a ping 172.17.0.3
# 方法二:telnet 特定端口
docker exec container-a sh -c "echo test | nc -w 2 172.17.0.3 80"
# 方法三:查看网络命名空间
lsns -t net
问:如何实现细粒度的网络隔离(比如只允许 HTTP 通信)?
Docker 自身的网络隔离只能做到”通或不通”层面的控制。更精细的隔离需要第三方网络插件(如 Calico、Cilium)或 sidecar 代理。
总结
网络隔离是分层的:命名空间提供基础隔离、网桥提供网络间隔离、自定义网络实现服务级隔离。核心原则是将不同功能的容器放在不同的网络中,通过”需要才连接”的方式实现最小权限网络隔离。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容