NetworkManager 运维教程 / 第 9 章:Docker 与容器网络
第 9 章:Docker 与容器网络
9.1 NM 与 Docker 的关系
NetworkManager 和 Docker 都会管理网络接口,如果不正确配置,可能会产生冲突。
常见冲突场景
| 冲突 | 现象 | 原因 |
|---|
| Docker bridge 接口被 NM 接管 | Docker 网络异常 | NM 默认管理所有接口 |
| 防火墙规则冲突 | 容器无法通信 | NM 和 Docker 都操作 iptables |
| DNS 冲突 | 容器 DNS 解析失败 | NM 重写 resolv.conf |
| docker0 接口频繁变化 | 网络不稳定 | NM 尝试管理 docker0 |
解决方案:让 NM 忽略容器接口
# 方法 1:通过配置文件忽略 docker 接口
sudo tee /etc/NetworkManager/conf.d/docker-unmanaged.conf << 'EOF'
[keyfile]
unmanaged-devices=interface-name:docker0;interface-name:veth*;interface-name:br-*;interface-name:virbr*
EOF
# 方法 2:忽略所有虚拟以太网设备
sudo tee /etc/NetworkManager/conf.d/unmanaged-veth.conf << 'EOF'
[keyfile]
unmanaged-devices=interface-name:veth*
EOF
# 方法 3:通过 udev 规则
sudo tee /etc/udev/rules.d/99-docker-nm-unmanaged.rules << 'EOF'
ENV{ID_NET_DRIVER}=="veth", ENV{NM_UNMANAGED}="1"
ENV{INTERFACE}=="docker*", ENV{NM_UNMANAGED}="1"
ENV{INTERFACE}=="br-*", ENV{NM_UNMANAGED}="1"
EOF
# 重新加载
sudo udevadm control --reload-rules
sudo udevadm trigger
sudo nmcli general reload conf
# 验证 docker0 不受 NM 管理
nmcli device status | grep docker
# docker0 bridge unmanaged --
9.2 Docker 默认网络与 NM
Docker 默认网络模型
┌─────────────────────────────────────────┐
│ 宿主机 │
│ │
│ ┌──────────────────────────────────┐ │
│ │ docker0 (172.17.0.1/16) │ │
│ │ Bridge 网络 │ │
│ └──────┬───────────┬───────────────┘ │
│ │ │ │
│ ┌────┴────┐ ┌────┴────┐ │
│ │ veth-xx │ │ veth-yy │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ ┌────┴────┐ ┌────┴────┐ │
│ │ 容器A │ │ 容器B │ │
│ │ 172.17 │ │ 172.17 │ │
│ │ .0.2 │ │ .0.3 │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────────┘
# 查看 Docker 网络
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# abc123... bridge bridge local
# def456... host host local
# ghi789... none null local
# 查看 bridge 网络详情
docker network inspect bridge
# 查看 docker0 接口
ip addr show docker0
brctl show docker0 # 或 ip link show master docker0
让 NM 管理 Docker bridge(可选)
如果你想让 NM 管理 Docker 的桥接网络:
# 创建 NM 管理的桥接
nmcli connection add \
type bridge \
con-name "docker-br" \
ifname docker-br \
ipv4.method manual \
ipv4.addresses "172.17.0.1/16" \
bridge.stp yes
# 配置 Docker 使用自定义桥接
# /etc/docker/daemon.json
{
"bridge": "docker-br",
"iptables": true
}
sudo systemctl restart docker
9.3 自定义 Docker 网络与 NM
创建自定义 Docker 网络
# 创建自定义桥接网络
docker network create \
--driver bridge \
--subnet 10.100.0.0/24 \
--gateway 10.100.0.1 \
--ip-range 10.100.0.128/25 \
--label environment=production \
my-network
# 创建隔离网络(无外部访问)
docker network create \
--driver bridge \
--internal \
--subnet 10.200.0.0/24 \
internal-net
# 使用自定义网络运行容器
docker run -d --name web --network my-network nginx
docker run -d --name app --network my-network my-app
# 连接已有容器到新网络
docker network connect my-network existing-container
# 断开网络
docker network disconnect my-network existing-container
NM 与自定义 Docker 网络的协调
# Docker 创建的 bridge 接口命名格式:br-<网络ID前12位>
# 例如 br-abc123def456
# 查看所有 Docker bridge
docker network ls --format "{{.ID}} {{.Name}} {{.Driver}}"
ip link show type bridge
# 确保 NM 忽略这些接口
# 更新 unmanaged 配置
sudo tee /etc/NetworkManager/conf.d/docker-unmanaged.conf << 'EOF'
[keyfile]
unmanaged-devices=interface-name:docker*;interface-name:br-*;interface-name:veth*
EOF
sudo nmcli general reload conf
9.4 macvlan 网络
macvlan 允许容器拥有独立的 MAC 地址,直接接入物理网络。
创建 macvlan 网络
# macvlan 让容器获得物理网络的 IP
# 假设宿主机 eth0 在 192.168.1.0/24 网段
# 创建 macvlan 网络
docker network create \
--driver macvlan \
--subnet 192.168.1.0/24 \
--gateway 192.168.1.1 \
-o parent=eth0 \
macvlan-net
# 使用 macvlan 运行容器
docker run -d --name macvlan-test \
--network macvlan-net \
--ip 192.168.1.200 \
nginx
# 验证容器获得正确的 IP
docker exec macvlan-test ip addr show eth0
NM 配合 macvlan
# 方案 1:使用 NM 创建 macvlan 子接口
nmcli connection add \
type macvlan \
con-name "macvlan0" \
ifname macvlan0 \
macvlan.parent eth0 \
macvlan.mode bridge \
ipv4.method manual \
ipv4.addresses "192.168.1.200/24"
# 方案 2:在 NM 管理的接口上创建 macvlan(Docker 内部处理)
# 通常不需要额外的 NM 配置
# 只需确保 NM 不干涉 macvlan 接口
# NM macvlan 模式说明
# bridge - 同一父接口的 macvlan 可以互相通信(推荐)
# vepa - 需要交换机支持 VEPA
# private - 不允许同父接口的 macvlan 互相通信
# passthru - 只允许一个 macvlan 绑定到父接口
9.5 NM 管理的桥接 + Docker
场景:使用 NM 桥接运行 Docker 容器
# 1. 创建 NM 管理的桥接
nmcli connection add \
type bridge \
con-name "br-docker" \
ifname br-docker \
ipv4.method manual \
ipv4.addresses "10.50.0.1/24"
# 2. 将物理接口加入桥接(可选)
nmcli connection add \
type ethernet \
con-name "br-docker-eth" \
ifname eth1 \
master br-docker \
slave-type bridge
# 3. 配置 Docker 使用该桥接
sudo tee /etc/docker/daemon.json << 'EOF'
{
"bridge": "br-docker",
"fixed-cidr": "10.50.0.0/24",
"dns": ["8.8.8.8"]
}
EOF
sudo systemctl restart docker
# 4. 验证
docker network inspect bridge | grep -i gateway
# "Gateway": "10.50.0.1"
优势
| 优势 | 说明 |
|---|
| 统一管理 | NM 管理所有网络配置 |
| 可视化 | nmcli 可以看到 Docker 网络 |
| 防火墙集成 | NM 可以配合 firewalld 管理规则 |
| 持久化 | 配置持久化在 NM 中 |
9.6 Docker Compose 网络配置
基本网络配置
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx
networks:
- frontend
- backend
ports:
- "80:80"
app:
image: my-app
networks:
- backend
environment:
- DB_HOST=db
db:
image: postgres
networks:
- backend
volumes:
- db-data:/var/lib/postgresql/data
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 10.10.1.0/24
gateway: 10.10.1.1
backend:
driver: bridge
internal: true # 不允许外部访问
ipam:
config:
- subnet: 10.10.2.0/24
volumes:
db-data:
使用外部网络(NM 管理的桥接)
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx
networks:
custom-net
networks:
custom-net:
external: true
name: br-docker # 使用 NM 创建的桥接
# 先创建外部网络
nmcli connection add type bridge con-name "br-docker" ifname br-docker \
ipv4.method manual ipv4.addresses "10.50.0.1/24"
# 或使用 Docker 创建(但 NM 不管理)
docker network create --driver bridge \
--subnet 10.50.0.0/24 br-docker
macvlan 与 Compose
version: '3.8'
services:
web:
image: nginx
networks:
macvlan-net:
ipv4_address: 192.168.1.200
networks:
macvlan-net:
driver: macvlan
driver_opts:
parent: eth0
ipam:
config:
- subnet: 192.168.1.0/24
gateway: 192.168.1.1
9.7 容器网络排障
# 1. 检查 NM 是否干扰 Docker 网络
nmcli device status | grep -i "docker\|br-\|veth"
# 期望:unmanaged
# 如果是 connected/disconnected → 需要配置忽略
# 2. 检查 Docker 网络
docker network ls
docker network inspect bridge
# 3. 检查容器网络
docker exec container_name ip addr show
docker exec container_name ip route
docker exec container_name cat /etc/resolv.conf
# 4. 检查桥接连通性
ping -c 3 172.17.0.1 # docker0
ping -c 3 172.17.0.2 # 容器
# 5. 检查端口映射
docker port container_name
ss -tlnp | grep docker
iptables -t nat -L -n | grep -i docker
# 6. 检查 DNS
docker exec container_name nslookup example.com
# 7. 检查 iptables 规则冲突
sudo iptables -L -n -v | head -50
# NM 的防火墙规则和 Docker 可能冲突
# 8. Docker 日志
journalctl -u docker -f
docker logs container_name
9.8 安全注意事项
| 事项 | 建议 |
|---|
| Docker 接口不被 NM 管理 | 配置 unmanaged-devices |
| 防火墙一致性 | 使用 firewalld 统一管理 |
| 容器网络隔离 | 使用 internal 网络 |
| 端口暴露 | 最小化端口映射 |
| MAC 地址 | 使用 Docker 默认随机 MAC |
# 配置 firewalld 与 Docker 共存
sudo firewall-cmd --permanent --zone=trusted --add-interface=docker0
sudo firewall-cmd --permanent --zone=trusted --add-masquerade
sudo firewall-cmd --reload
9.9 本章小结
| 要点 | 说明 |
|---|
| 核心原则 | 让 NM 忽略 Docker 管理的接口 |
| 配置方式 | conf.d/ 中设置 unmanaged-devices |
| macvlan | 容器直接接入物理网络 |
| NM 桥接 | 可以让 Docker 使用 NM 管理的桥接 |
| Compose | 支持 external 网络引用 NM 桥接 |
| 排障 | 先检查 NM 是否干扰了 Docker 接口 |
扩展阅读