Docker常见问题

1. 生产环境的 Docker 安装过程(以 Linux 系统为例)

生产环境需优先选择官方源 + 稳定版,避免依赖第三方源导致兼容性问题,以 CentOS 7/8 或 Ubuntu 20.04+ 为例,步骤如下:

  1. 环境预处理

    • 卸载旧版本(若存在):sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine(CentOS)或 sudo apt-get remove docker docker-engine docker.io containerd runc(Ubuntu)。
    • 安装依赖工具:sudo yum install -y yum-utils device-mapper-persistent-data lvm2(CentOS)或 sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common(Ubuntu)。
  2. 配置官方源

    • CentOS:sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    • Ubuntu:curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -,再添加源:sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
  3. 安装 Docker Engine

    • 安装稳定版:sudo yum install docker-ce docker-ce-cli containerd.io(CentOS)或 sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io(Ubuntu)。
    • 验证安装:sudo docker --version,输出类似 Docker version 26.0.0, build 2ae903e 即成功。
  4. 生产环境基础配置

    • 启动并设置开机自启:sudo systemctl start docker && sudo systemctl enable docker

    • 配置非 root 用户操作(避免权限风险):sudo usermod -aG docker $USER(需重新登录生效)。

    • (可选)配置镜像加速器(解决国内拉取慢问题):编辑 /etc/docker/daemon.json,添加阿里云 / 网易等加速器地址,如:

      json

      复制代码
      { "registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"] }

      重启服务:sudo systemctl daemon-reload && sudo systemctl restart docker

2. 容器运行时(Container Runtime)简略说明

容器运行时是负责容器生命周期管理的核心组件,本质是 "将容器镜像加载为可运行容器实例,并执行隔离、资源限制" 的工具,是 Docker、Kubernetes 等容器平台的 "底层引擎"。

  • 核心功能

    1. 镜像解析:将容器镜像(如 Docker Image)解压为文件系统层;
    2. 容器创建:通过 Linux Namespace/CGroups 实现进程隔离与资源限制;
    3. 生命周期管理:启动、停止、重启、删除容器实例;
    4. 日志与监控:收集容器输出日志,暴露资源使用 metrics。
  • 常见类型

    • 低级运行时(如 runc):Docker 默认运行时,仅负责 "容器进程启停",依赖 Linux 内核特性;
    • 高级运行时(如 containerd):封装 runc,增加镜像管理、网络配置、存储挂载等功能,是 Kubernetes 的主流运行时;
    • 其他运行时(如 CRI-Okata-containers):适配 Kubernetes CRI 标准,或通过轻量虚拟机增强安全性。

3. 容器镜像(Container Image)及结构

定义

容器镜像是只读的、可移植的文件集合,包含运行容器所需的所有内容:代码、运行时(如 JRE/Python)、库文件、环境变量、配置文件,是 "容器实例的模板"(一个镜像可创建多个容器)。

结构(分层结构,基于 UnionFS)

镜像采用 "分层叠加 " 设计,每一层对应 Dockerfile 中的一条指令(如 FROMRUNCOPY),所有层联合组成一个统一的文件系统,结构如下:

  1. 基础层(Base Layer) :最底层,通常是官方基础镜像(如 ubuntu:22.04alpine:3.18),包含操作系统核心文件(如 /bin/etc);
  2. 中间层(Intermediate Layers) :Dockerfile 中除最后一条外的指令生成的层(如 RUN apt install nginx 会生成一层),每一层都是只读的,且可被多个镜像共享(如多个镜像基于同一 ubuntu 基础层,仅存储一份基础层,节省磁盘空间);
  3. 可写层(Writable Layer) :容器启动时在镜像顶层添加的临时可写层,容器内的文件修改(如创建、删除、修改文件)仅发生在这一层;容器销毁时,可写层也会被删除,镜像本身不变;
  4. 元数据(Metadata) :即 image manifestconfig 文件,记录镜像的分层信息、环境变量、启动命令(CMD/ENTRYPOINT)、端口暴露等配置,Docker 通过元数据解析镜像如何运行。

4. 企业选择容器技术的理由

容器技术解决了传统部署 "环境不一致""资源利用率低""运维复杂" 三大核心痛点,企业选择的核心理由如下:

  1. 环境一致性(消除'Works on my machine'):镜像包含所有依赖,从开发、测试到生产环境使用同一镜像,避免因依赖版本、配置差异导致的部署失败;
  2. 资源高效利用:容器共享宿主机内核,启动时仅加载必要文件(无虚拟机的操作系统开销),单台宿主机可运行数百个容器,资源利用率比虚拟机提升 30%-50%;
  3. 快速启停与弹性伸缩:容器启动时间通常在秒级(虚拟机需分钟级),配合 Kubernetes 等编排工具,可快速应对业务流量波动(如秒杀场景自动扩容);
  4. 简化运维与 CI/CD 集成:容器可通过 Dockerfile 代码化定义,与 Git、Jenkins 等工具无缝集成,实现 "代码提交→自动构建镜像→自动部署" 的 DevOps 闭环;
  5. 隔离性与安全性:通过 Namespace 实现进程、网络、文件系统隔离,通过 CGroups 限制资源使用,避免单个应用故障影响其他应用(隔离性弱于虚拟机,但满足绝大多数业务场景);
  6. 可移植性强:镜像可在任何支持容器运行时的环境(物理机、虚拟机、公有云、私有云)中运行,避免厂商锁定(如从 AWS 迁移到阿里云无需修改镜像)。

5. UnionFS 的实现原理(完整说明)

UnionFS(联合文件系统)是将多个独立的只读文件系统层(Branch)"联合挂载" 为一个统一的可读写文件系统的技术,核心目标是 "分层复用、写时复制(Copy-On-Write, CoW)",是容器镜像分层结构的底层支撑。

1. 核心概念
  • Branch(分支) :UnionFS 挂载的多个原始文件系统层,分为两类:
    • ReadOnly Branch(只读分支):容器镜像的基础层、中间层,不可修改;
    • Writeable Branch(可写分支):容器启动时添加的顶层,仅用于存储容器运行中的修改;
  • CoW(写时复制):当需要修改只读分支中的文件时,UnionFS 先将该文件从只读分支复制到可写分支,再修改可写分支中的副本,避免直接修改只读分支(保证镜像层可复用);
  • Union View(联合视图):用户 / 应用看到的统一文件系统,由所有分支的文件 "合并" 而成,同名文件以 "上层覆盖下层" 为原则(可写分支优先级最高,其次是上层只读分支)。
2. 工作流程(以容器读写文件为例)

假设容器镜像包含 3 个只读分支(Layer1→Layer2→Layer3,Layer3 为最上层只读层),容器启动时添加 1 个可写分支(Writable Layer),联合视图为 Writable Layer → Layer3 → Layer2 → Layer1

  1. 读文件操作

    • 应用请求读取 /etc/nginx/nginx.conf,UnionFS 从最高优先级的 Writable Layer 开始查找;
    • 若 Writable Layer 中无该文件,依次向下查找 Layer3、Layer2、Layer1;
    • 找到文件后,直接返回给应用(用户感知不到分层,仅看到 "一个文件")。
  2. 写文件操作(修改已有文件)

    • 应用请求修改 /etc/nginx/nginx.conf(该文件实际在 Layer2 中);
    • UnionFS 触发 CoW 机制:先将 Layer2 中的 nginx.conf 复制到 Writable Layer;
    • 应用仅修改 Writable Layer 中的 nginx.conf 副本;
    • 后续读操作会优先读取 Writable Layer 中的修改后文件(覆盖下层只读层的原文件)。
  3. 创建新文件操作

    • 应用创建 /var/log/nginx/access.log,UnionFS 直接在 Writable Layer 中创建该文件,不影响任何只读分支。
  4. 删除文件操作

    • 应用删除 /usr/bin/ls(该文件在 Layer1 中);
    • UnionFS 不实际删除 Layer1 中的 ls(只读层不可修改),而是在 Writable Layer 中创建一个 "删除标记(Whiteout)";
    • 后续读操作时,UnionFS 识别到 Whiteout 标记,会 "隐藏" 下层只读分支中的 ls,应用感知到 "文件已删除"。
3. 关键特性
  • 分层复用 :多个容器可共享同一套只读镜像层(如 10 个 Nginx 容器共享同一个 nginx:alpine 镜像的 3 个只读层),大幅节省磁盘空间;
  • 轻量写操作:仅复制被修改的文件(而非整个层),减少 IO 开销,提升容器启动速度;
  • 隔离性:容器的修改仅保存在自身的可写层,多个容器基于同一镜像运行时,修改互不干扰。
4. 常见实现(Docker 支持的 UnionFS 变体)

Docker 早期支持 aufs,目前主流使用 overlay2(Linux 内核 3.18 + 支持),此外还有 devicemapperbtrfs 等,其中 overlay2 因性能优、配置简单成为默认存储驱动。

6. Docker 和 Overlay2、UnionFS 的关系

三者是 "上层工具→具体实现→底层标准" 的关系,具体如下:

  1. UnionFS :是 "联合文件系统" 的技术标准 / 概念,定义了 "分层联合、写时复制" 的核心思想,是容器镜像分层的理论基础;
  2. Overlay2 :是 UnionFS 的一种具体实现 (Linux 内核支持的文件系统驱动),解决了早期 aufs 的兼容性问题,目前是 Docker 的默认存储驱动(Docker 18.09+ 推荐);
  3. Docker :是容器平台,依赖 Overlay2(或其他 UnionFS 变体)实现镜像分层与容器可写层管理 ------Docker 将镜像的多个只读层作为 Overlay2 的 "lowerdir"(只读分支),容器的可写层作为 "upperdir"(可写分支),通过 Overlay2 将这些层联合挂载为容器的根文件系统,供容器内进程使用。

简单总结:UnionFS 是 "思想",Overlay2 是 "落地工具",Docker 是 "使用者"------Docker 通过 Overlay2 实现了 UnionFS 的分层与 CoW 特性,从而支撑镜像和容器的运行。

7. 最熟悉的构建 Docker 镜像的方法:Dockerfile + docker build

这是最主流、最可复用的构建方法,通过代码化的 Dockerfile 定义镜像分层 ,配合 docker build 命令生成镜像,步骤清晰且支持版本控制(可纳入 Git 仓库)。

1. 核心流程
  1. 编写 Dockerfile :在项目根目录创建 Dockerfile 文件,通过指令定义镜像的基础层、依赖安装、代码复制、启动命令等(示例:构建一个 Python Flask 应用镜像):

    dockerfile

    复制代码
    # 1. 基础层:指定Python官方基础镜像(只读层)
    FROM python:3.10-slim
    
    # 2. 设置工作目录(中间层)
    WORKDIR /app
    
    # 3. 复制依赖文件并安装(中间层)
    COPY requirements.txt .  # 复制本地requirements.txt到容器/app目录
    RUN pip install --no-cache-dir -r requirements.txt  # 安装Flask依赖
    
    # 4. 复制应用代码(中间层)
    COPY app.py .  # 复制本地Flask应用代码
    
    # 5. 暴露端口(元数据,不实际映射)
    EXPOSE 5000
    
    # 6. 定义启动命令(元数据)
    CMD ["python", "app.py"]

    (注:每一行指令对应一个镜像层,FROM 是基础层,RUN/COPY 等生成中间层,EXPOSE/CMD 仅修改元数据,不生成新层)。

  2. 构建镜像 :在 Dockerfile 所在目录执行 docker build 命令,指定镜像名称(-t)和标签:

    bash

    复制代码
    docker build -t flask-app:v1.0 .  # . 表示当前目录为构建上下文(Docker会读取该目录下的文件)

    构建过程中,Docker 会按 Dockerfile 指令顺序创建分层,且会缓存已生成的层(若后续构建时指令未修改,直接复用缓存层,加速构建)。

  3. 验证镜像 :通过 docker images 查看镜像,通过 docker run -p 5000:5000 flask-app:v1.0 启动容器,验证应用是否正常运行。

2. 关键优化技巧
  • 合并 RUN 指令 :减少中间层数量(如 RUN apt update && apt install -y nginx && rm -rf /var/lib/apt/lists/*,避免拆分多条 RUN 生成多余层);
  • 使用 .dockerignore 文件 :排除构建上下文内不需要的文件(如 node_modules.git),减少构建上下文大小,加速镜像传输;
  • 选择轻量基础镜像 :如用 alpine 代替 ubuntualpine 镜像仅几 MB,ubuntu 约 200MB),减少镜像体积。

8. 容器隔离用到的 Linux Namespace

Linux Namespace 是Linux 内核提供的 "进程隔离" 机制,通过为进程分配独立的 "命名空间",使其仅能看到命名空间内的资源(如进程、网络、文件系统),从而实现容器与宿主机、容器与容器之间的隔离。Docker 依赖 6 类 Namespace 实现核心隔离,如下表:

Namespace 类型 隔离对象 作用说明
PID Namespace 进程 ID(PID) 容器内的进程有独立的 PID 编号(如容器内的init进程 PID 为 1),无法看到宿主机其他进程
Network Namespace 网络设备、IP、端口、路由 容器有独立的网络栈(如独立的网卡、IP 地址、端口范围),需通过桥接 / 端口映射与外部通信
Mount Namespace 文件系统挂载点 容器内的挂载操作仅影响自身(如挂载 /tmp),不修改宿主机的文件系统结构
UTS Namespace 主机名(hostname)、域名 容器可设置独立的主机名(如 nginx-container),与宿主机和其他容器区分
IPC Namespace 进程间通信资源(信号量、消息队列) 容器内的进程仅能与同一容器内的进程通信,无法访问其他容器的 IPC 资源
User Namespace 用户 ID(UID)、组 ID(GID) 容器内的 root 用户(UID=0)映射到宿主机的普通用户,避免容器内 root 权限逃逸

9. 手工创建一个类容器对象的操作(基于 Linux 命令)

手工创建类容器对象,本质是通过 unshare/clone 命令为进程分配独立 Namespace,并通过 cgroups 限制资源,步骤如下(以创建一个隔离的 Bash 进程为例):

1. 准备环境(宿主机需为 Linux,且有 root 权限)

确保内核支持 Namespace(ls /proc/$$/ns 可查看当前进程的 Namespace)。

2. 创建独立的 Mount + PID + UTS + IPC + Network Namespace

通过 unshare 命令创建新进程,并分配 5 类 Namespace(User Namespace 需额外配置,暂不演示):

bash

复制代码
# --mount:创建独立Mount Namespace
# --pid:创建独立PID Namespace(需配合--fork,否则无法启动新进程)
# --uts:创建独立UTS Namespace
# --ipc:创建独立IPC Namespace
# --net:创建独立Network Namespace
# --fork:在新Namespace中fork一个子进程
# --mount-proc:在新Mount Namespace中挂载/proc(让容器内看到自己的进程)
# bash:新Namespace中运行的进程(即容器内的"shell")
sudo unshare --mount --pid --uts --ipc --net --fork --mount-proc bash

执行后,会进入一个 "类容器" 的 Bash 环境,此时:

  • hostname 可修改(如 hostname container-01),不影响宿主机;
  • ps aux 仅能看到当前 Bash 进程(PID=1)和 ps 命令进程;
  • ip addr 仅能看到 lo 回环网卡(无其他网络设备)。
3. 配置网络(让类容器能联网)

默认 Network Namespace 仅有lo网卡,需手动创建虚拟网卡对(veth pair)连接宿主机网桥:

bash

复制代码
# 1. 在宿主机执行(另开一个终端):创建veth pair(veth-host 和 veth-container)
sudo ip link add veth-host type veth peer name veth-container

# 2. 将veth-host加入宿主机默认网桥(docker0,若不存在可手动创建)
sudo brctl addif docker0 veth-host
sudo ip link set veth-host up

# 3. 将veth-container移入类容器的Network Namespace(需替换<container-pid>为类容器Bash的PID)
# 查看类容器PID:宿主机执行 `ps aux | grep bash`,找到unshare启动的bash进程PID
sudo ip link set veth-container netns <container-pid>

# 4. 回到类容器Bash,配置veth-container的IP并启动
ip link set veth-container up
ip addr add 172.17.0.10/16 dev veth-container  # 与docker0同网段(默认172.17.0.0/16)
ip route add default via 172.17.0.1  # 网关指向docker0的IP(172.17.0.1)

# 5. 配置DNS(让类容器能解析域名)
echo "nameserver 8.8.8.8" > /etc/resolv.conf

此时类容器可通过 ping 172.17.0.1curl baidu.com 联网。

4. 挂载根文件系统(模拟容器镜像层)

类容器默认使用宿主机的根文件系统,需手动挂载一个只读目录作为 "基础层",再挂载一个可写目录作为 "可写层"(模拟 UnionFS):

bash

复制代码
# 1. 宿主机创建基础层目录(如alpine基础镜像文件)和可写层目录
sudo mkdir -p /container/base /container/writable

# 2. 类容器中挂载基础层(只读)和可写层(读写),并通过overlay挂载为联合目录
sudo mount -t overlay overlay -o lowerdir=/container/base,upperdir=/container/writable,workdir=/container/work /container/root
sudo chroot /container/root  # 切换根目录到联合目录,此时看到的文件系统为基础层+可写层
5. 限制资源(通过 cgroups)

bash

复制代码
# 1. 宿主机创建cgroups目录(限制CPU和内存)
sudo mkdir -p /sys/fs/cgroup/cpu/container01 /sys/fs/cgroup/memory/container01

# 2. 设置CPU限制(最多使用1个CPU核心的50%)
echo 50000 > /sys/fs/cgroup/cpu/container01/cpu.cfs_quota_us  # 配额50ms
echo 100000 > /sys/fs/cgroup/cpu/container01/cpu.cfs_period_us  # 周期100ms

# 3. 设置内存限制(最多使用512MB)
echo 536870912 > /sys/fs/cgroup/memory/container01/memory.limit_in_bytes

# 4. 将类容器进程加入cgroups(替换<container-pid>)
echo <container-pid> > /sys/fs/cgroup/cpu/container01/cgroup.procs
echo <container-pid> > /sys/fs/cgroup/memory/container01/cgroup.procs
6. 验证隔离效果
  • 类容器内 ps aux 仅显示自身进程;
  • 类容器内修改文件仅影响可写层,不影响宿主机;
  • 类容器 CPU / 内存使用超限时会被限制。

10. CGroups 对于容器运行的重要作用

CGroups(Control Groups,控制组)是Linux 内核提供的 "资源限制与监控" 机制,核心作用是 "为一组进程分配可量化的系统资源(CPU、内存、IO 等),并限制其最大使用量",是容器 "资源隔离" 的核心支撑(Namespace 解决 "看到的资源不同",CGroups 解决 "使用的资源有限")。

其对容器运行的重要作用如下:

  1. 防止资源滥用:限制单个容器的 CPU、内存、磁盘 IO 等资源上限,避免某一容器因异常(如内存泄漏、无限循环)耗尽宿主机资源,导致其他容器或宿主机崩溃;
  2. 资源配额分配:按业务优先级为容器分配资源(如给核心服务容器分配 2 个 CPU 核心、8GB 内存,给非核心服务分配 0.5 个 CPU、2GB 内存),保证资源合理利用;
  3. 资源监控:实时统计容器的资源使用情况(如 CPU 使用率、内存占用、IO 吞吐量),为运维监控(如 Prometheus)和弹性伸缩提供数据支撑;
  4. 资源隔离强化:配合 Namespace 实现 "既看不到、也用不完" 的完整隔离 ------Namespace 隔离 "资源可见性",CGroups 隔离 "资源可用性";
  5. 支持多租户场景:在 Kubernetes 等平台中,通过 CGroups 为不同租户(团队 / 业务线)的容器分配独立资源池,实现资源隔离与成本核算。

11. CGroups 控制容器资源使用的方式(举例说明)

CGroups 通过 "子系统(Subsystem) " 控制不同类型的资源,每个子系统对应一种资源(如 CPU 子系统控制 CPU,内存子系统控制内存),通过 "将容器进程加入对应子系统的控制组(CGroup),并配置控制组的资源参数" 实现限制。

以下以CPU 子系统内存子系统为例,说明控制方式:

1. 核心原理
  • 子系统(Subsystem) :对应一种资源的控制器,如 cpu(CPU)、memory(内存)、blkio(块设备 IO)、net_cls(网络带宽)等;
  • 控制组(CGroup) :每个子系统下的资源限制单元,可创建层级结构(如 /sys/fs/cgroup/cpu/apps/webapps 组下的 web 子组);
  • 进程关联 :将容器内所有进程的 PID 写入控制组的 cgroup.procs 文件,该控制组的资源限制会作用于所有关联进程。
2. 示例 1:CPU 资源控制(限制容器最多使用 1 个 CPU 核心的 50%)

假设宿主机有 4 个 CPU 核心(0-3),通过 cpu 子系统配置:

  1. 创建控制组

    bash

    复制代码
    sudo mkdir -p /sys/fs/cgroup/cpu/my-container  # 在cpu子系统下创建my-container控制组
  2. 配置 CPU 配额(CFS Quota)

    • cpu.cfs_period_us:CPU 调度周期(默认 100000 微秒 = 100ms);
    • cpu.cfs_quota_us:周期内允许使用的 CPU 时间(配额),若设为 50000 微秒 = 50ms,表示周期内最多使用 50% 的 CPU(1 个核心的 50%);

    bash

    复制代码
    sudo echo 100000 > /sys/fs/cgroup/cpu/my-container/cpu.cfs_period_us  # 周期100ms
    sudo echo 50000 > /sys/fs/cgroup/cpu/my-container/cpu.cfs_quota_us   # 配额50ms
  3. 配置 CPU 核心绑定(可选):限制容器仅使用指定 CPU 核心(如核心 0),避免 CPU 上下文切换开销:

    bash

    复制代码
    sudo echo 1 > /sys/fs/cgroup/cpu/my-container/cpu.cpuset.cpus  # 仅允许使用CPU核心0(二进制1对应核心0)
  4. 关联容器进程:假设容器内主进程 PID 为 1234,将其加入控制组:

    bash

    复制代码
    sudo echo 1234 > /sys/fs/cgroup/cpu/my-container/cgroup.procs
  5. 验证 :容器内运行 CPU 密集型程序(如 while true; do :; done),通过 topdocker stats 查看,CPU 使用率会被限制在 50% 左右。

3. 示例 2:内存资源控制(限制容器最多使用 512MB 内存)

通过 memory 子系统配置:

  1. 创建控制组

    bash

    复制代码
    sudo mkdir -p /sys/fs/cgroup/memory/my-container
  2. 配置内存上限

    • memory.limit_in_bytes:容器可使用的最大物理内存(单位:字节),512MB=51210241024=536870912 字节;
    • memory.swappiness:设置为 0,禁止容器使用交换分区(避免内存不足时使用 swap 导致性能下降);

    bash

    复制代码
    sudo echo 536870912 > /sys/fs/cgroup/memory/my-container/memory.limit_in_bytes  # 512MB
    sudo echo 0 > /sys/fs/cgroup/memory/my-container/memory.swappiness  # 禁用swap
  3. 关联容器进程

    bash

    复制代码
    sudo echo 1234 > /sys/fs/cgroup/memory/my-container/cgroup.procs
  4. 验证 :容器内运行内存密集型程序(如 Python 代码 a = [i for i in range(10**8)]),当内存使用超 512MB 时,内核会触发 OOM(Out Of Memory)杀死容器进程,避免占用更多内存。

12. Docker 网络模式及适用场景

Docker 提供 5 种核心网络模式,通过 docker run --net=<模式> 指定,不同模式对应不同的网络隔离与通信能力,适用场景如下表:

网络模式 名称 核心特点 适用场景
bridge 桥接模式 容器使用独立 Network Namespace,通过虚拟网卡对(veth pair)连接宿主机docker0网桥,默认分配 172.17.0.0/16 网段 IP,需端口映射(-p)与外部通信 最常用场景:多个容器在同一宿主机内通信(如 Web 容器 + 数据库容器),且需对外提供服务(如暴露 80 端口)
host 主机模式 容器不创建独立 Network Namespace,共享宿主机网络栈(使用宿主机 IP 和端口),无网络隔离 需高性能网络场景(如网络密集型服务),或需直接使用宿主机端口(如避免端口映射冲突)
none 无网络模式 容器创建独立 Network Namespace,但不配置任何网络(仅lo回环网卡),完全隔离 无需网络的场景(如离线数据处理容器),或需手动配置复杂网络(如自定义虚拟网卡、路由)
container:<name/id> 容器模式 新容器共享指定容器的 Network Namespace(与指定容器使用同一 IP 和端口),两者网络完全互通 需 "父子容器" 通信场景(如 Sidecar 模式:主容器 + 日志收集容器,共享网络以便日志收集)
overlay 覆盖网络模式 跨宿主机的容器网络,通过 VXLAN 技术将多个宿主机的docker0网桥连接为一个虚拟网络,容器可跨宿主机通信(需 Docker Swarm 或 Kubernetes 支持) 分布式应用场景(如微服务部署在多台宿主机,容器需跨主机通信,如 K8s 集群内 Pod 通信)

13. Bridge 模式下容器网络流量的转发过程

Bridge 模式是 Docker 默认网络模式,核心是通过宿主机的docker0网桥实现容器与宿主机、容器与容器、容器与外部网络的通信,以下分 "容器→外部网络""外部网络→容器""容器→容器" 三种场景说明转发过程。

前提:Bridge 模式网络拓扑
  • 宿主机有一个虚拟网桥 docker0(默认 IP:172.17.0.1/16);
  • 每个容器启动时,Docker 创建一对虚拟网卡(veth pair):一端为容器内的 eth0(IP:172.17.0.x/16,网关:172.17.0.1),另一端为宿主机的 vethxxxx(挂载到 docker0 网桥);
  • 宿主机开启 IP 转发net.ipv4.ip_forward=1),支持数据包在不同网卡间转发;
  • 容器对外通信依赖宿主机的 SNAT(源地址转换) ,外部访问容器依赖 DNAT(目的地址转换)
1. 场景 1:容器→外部网络(如容器访问百度)

假设容器 A 的 IP 为 172.17.0.2,访问 www.baidu.com(IP:180.101.49.12),流程如下:

  1. 容器内数据包生成 :容器 A 的应用(如curl)生成 TCP 数据包,目的 IP=180.101.49.12,目的端口 = 80,源 IP=172.17.0.2,源端口 = 随机端口(如 34567);
  2. 容器内路由 :容器 A 的路由表(网关 = 172.17.0.1)判断目的 IP 不在本地网段,将数据包发送到 eth0 网卡;
  3. veth pair 转发 :数据包通过 veth pair 从容器内 eth0 发送到宿主机的 vethxxxx 网卡;
  4. docker0 网桥转发vethxxxx 挂载到 docker0 网桥,docker0 接收数据包后,根据目的 IP(180.101.49.12)转发到宿主机的物理网卡(如 eth0,IP:192.168.1.100);
  5. 宿主机 SNAT 转换 :宿主机的iptables规则(Docker 自动配置)触发 SNAT,将数据包的源 IP 从 172.17.0.2 替换为宿主机物理网卡 IP(192.168.1.100),源端口保持不变;
  6. 外部网络转发:转换后的数据包通过宿主机物理网卡发送到路由器,再转发到百度服务器;
  7. 响应数据包返回:百度服务器返回 TCP 响应数据包(目的 IP=192.168.1.100,目的端口 = 34567),通过路由器转发到宿主机物理网卡;
  8. 宿主机 DNAT 转换(反向) :宿主机iptables根据连接跟踪(conntrack)记录,将目的 IP 从 192.168.1.100 替换为容器 A 的 IP(172.17.0.2),目的端口替换为 34567;
  9. docker0 网桥转发到容器 :数据包通过docker0网桥转发到宿主机的vethxxxx网卡,再通过 veth pair 发送到容器 A 的eth0,最终被应用接收。
2. 场景 2:外部网络→容器(如外部访问容器内 Web 服务)

假设容器 B 运行 Nginx 服务(端口 80),通过 docker run -p 8080:80 映射宿主机 8080 端口到容器 80 端口,外部主机(IP:192.168.1.200)访问 http://192.168.1.100:8080,流程如下:

  1. 外部数据包生成:外部主机生成 TCP 数据包,目的 IP = 宿主机物理 IP(192.168.1.100),目的端口 = 8080,源 IP=192.168.1.200,源端口 = 随机端口(如 56789);
  2. 宿主机接收数据包 :数据包通过宿主机物理网卡(eth0)接收,宿主机内核判断目的端口 = 8080,触发iptables的 DNAT 规则;
  3. 宿主机 DNAT 转换 :Docker 预配置的iptables规则将数据包的目的 IP 从 192.168.1.100 替换为容器 B 的 IP(172.17.0.3),目的端口从 8080 替换为 80;
  4. docker0 网桥转发到容器 :转换后的数据包通过docker0网桥转发到宿主机的vethyyyy网卡(容器 B 的 veth pair 另一端),再发送到容器 B 的eth0
  5. 容器内处理:容器 B 的 Nginx 服务(监听 80 端口)接收数据包,处理后返回 TCP 响应(目的 IP=192.168.1.200,目的端口 = 56789,源 IP=172.17.0.3,源端口 = 80);
  6. 响应数据包返回外部 :响应数据包按 "场景 1" 的反向流程,通过 veth pair→docker0→宿主机物理网卡,经 SNAT 转换源 IP 为 192.168.1.100 后,返回外部主机。
3. 场景 3:同一宿主机内容器→容器(如容器 A 访问容器 B 的数据库)

假设容器 A(172.17.0.2)访问容器 B(172.17.0.3,运行 MySQL 服务,端口 3306),流程如下:

  1. 容器 A 生成数据包:源 IP=172.17.0.2,源端口 = 随机端口,目的 IP=172.17.0.3,目的端口 = 3306;
  2. 容器 A 路由 :目的 IP(172.17.0.3)在同一网段(172.17.0.0/16),容器 A 通过 ARP 协议获取容器 B 的eth0网卡 MAC 地址;
  3. veth pair→docker0 转发 :数据包通过容器 A 的eth0→宿主机vethxxxxdocker0网桥,docker0根据 MAC 地址将数据包转发到容器 B 的vethyyyy
  4. 容器 B 接收 :数据包通过vethyyyy→容器 B 的eth0,被 MySQL 服务接收并处理;
  5. 响应返回:容器 B 返回的响应数据包按原路返回容器 A,无需经过宿主机物理网卡,通信效率高。
关键技术点
  • veth pair:容器与宿主机的 "网络桥梁",实现数据包双向传输;
  • docker0 网桥:同一宿主机内容器的 "交换中心",实现容器间二层通信;
  • iptables NAT:实现容器与外部网络的通信(SNAT/DNAT),是 Bridge 模式对外通信的核心;
  • IP 转发 :宿主机内核需开启net.ipv4.ip_forward=1,否则数据包无法在docker0与物理网卡间转发。
相关推荐
佳児素花痴╮18 分钟前
C语言链接库
linux·运维·服务器
雷电小将军29 分钟前
诡秘之主手游占卜家序列加点 诡秘之主手游占卜家怎么加点
服务器·网络·游戏·电脑·玩游戏
云飞云共享云桌面33 分钟前
苏州昆山精密机械加工厂研发部门10个SolidWorks可以部署云飞云共享云桌面吗?
大数据·运维·服务器·自动化·汽车·制造
聚美智数34 分钟前
图片水印-图片剪裁-图片缩放API接口介绍
java·服务器·数据库
云飞云共享云桌面1 小时前
自动化设备厂 10 人研发团队,如何共用一台服务器做 SolidWorks 建模?
运维·服务器·网络·3d·自动化·制造
筝筝ba2 小时前
Configure IP Address Via Different User Roles And Verify(1)
服务器
QH139292318802 小时前
NVIDIA H200 H300服务器
运维·服务器·开发语言·网络·信息与通信
xixiaoyunya2 小时前
中小微企业轻量组网方案的技术选型与实践:从传统专线到软定义隧道的成本与效能分析
服务器·php·apache
Hello-FPGA2 小时前
嵌入式LINUX 设备 ip配置后怎么保存才能掉电不消失?
linux·运维·服务器
奥莱维2 小时前
KNX酒店方案_KNX专用线与高端酒店技术逻辑
java·服务器·前端·数据库