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与物理网卡间转发。
相关推荐
一匹电信狗3 小时前
【MySQL】数据库表的操作
linux·运维·服务器·数据库·mysql·ubuntu·小程序
撬动未来的支点3 小时前
【Linux】Linux 零拷贝技术全景解读:从内核到硬件的性能优化之道
linux·服务器·性能优化
ajassi20003 小时前
开源 Linux 服务器与中间件(六)服务器--Lighttpd
linux·服务器·开源
荣光波比4 小时前
K8S(十七)—— Kubernetes集群可视化工具Kuboard部署与实践指南
云原生·容器·kubernetes
milanyangbo4 小时前
从C10K到Reactor:事件驱动,如何重塑高并发服务器的网络架构
服务器·网络·后端·架构
爱吃生蚝的于勒4 小时前
【Linux】深入理解进程(一)
java·linux·运维·服务器·数据结构·c++·蓝桥杯
嫄码4 小时前
Docker部署RocketMQ时Broker IP地址问题及解决方案
tcp/ip·docker·rocketmq
月球挖掘机4 小时前
华为USG防火墙之开局上网配置
服务器·网络
噔噔君4 小时前
嵌入式模组拨号获取IP地址时,设置的ippass是什么原理,起到什么作用?
服务器·网络协议·tcp/ip·ip