强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 接口

扩展阅读