Docker 容器与网络深度指南:从入门到精通

本文系统梳理 Docker 容器的核心操作、资源管控、底层实现机制以及网络模型,适合希望深入理解 Docker 内核原理的开发者与运维工程师。

文章目录

    • 一、容器
      • [1. 如何运行容器](#1. 如何运行容器)
        • [示例:create + start 分步执行](#示例:create + start 分步执行)
        • [示例:docker run 一步到位](#示例:docker run 一步到位)
      • [2. 让容器长期运行](#2. 让容器长期运行)
      • [3. 两种进入容器的方法](#3. 两种进入容器的方法)
        • [docker attach](#docker attach)
        • [docker exec](#docker exec)
        • [attach 与 exec 的核心区别](#attach 与 exec 的核心区别)
      • [4. 运行容器的最佳实践](#4. 运行容器的最佳实践)
      • [5. 容器常用操作](#5. 容器常用操作)
      • [6. 一张图搞懂容器状态机](#6. 一张图搞懂容器状态机)
      • [7. 限制容器对内存的使用](#7. 限制容器对内存的使用)
      • [8. 限制容器对 CPU 的使用](#8. 限制容器对 CPU 的使用)
      • [9. export 和 import 容器](#9. export 和 import 容器)
        • [docker export ------ 容器导出为 tar 包](#docker export —— 容器导出为 tar 包)
        • [docker import ------ 将 tar 包导入为镜像](#docker import —— 将 tar 包导入为镜像)
        • [docker save vs docker export 对比](#docker save vs docker export 对比)
      • [10. 实现容器的底层技术](#10. 实现容器的底层技术)
        • [cgroup 深入](#cgroup 深入)
        • [namespace ------ 资源隔离的基石](#namespace —— 资源隔离的基石)
    • 二、网络
      • [1. Docker 网络概览](#1. Docker 网络概览)
      • [2. none 网络](#2. none 网络)
      • [3. host 网络](#3. host 网络)
      • [4. Bridge 网络(核心重点)](#4. Bridge 网络(核心重点))
      • [5. 自定义容器网络](#5. 自定义容器网络)
        • [创建自定义 bridge 网络](#创建自定义 bridge 网络)
        • 使用自定义网络
        • [指定静态 IP](#指定静态 IP)
      • [6. 容器之间的连通性](#6. 容器之间的连通性)
      • [7. 容器通信的三种方式](#7. 容器通信的三种方式)
        • [方式一:IP 通信](#方式一:IP 通信)
        • [方式二:Docker DNS Server](#方式二:Docker DNS Server)
        • [方式三:Joined 容器](#方式三:Joined 容器)
      • [8. 容器如何访问外部世界](#8. 容器如何访问外部世界)
      • [9. 外部世界如何访问容器](#9. 外部世界如何访问容器)
    • 三、总结

一、容器

1. 如何运行容器

docker run 是启动容器的核心命令,其本质等价于 docker create + docker start 的组合。

在讨论 Dockerfile 时我们已经了解到,可以通过三种方式指定容器启动时执行的命令:

  1. CMD 指令
  2. ENTRYPOINT 指令
  3. docker run 命令行中直接指定
示例:create + start 分步执行
bash 复制代码
# 使用 ubuntu 镜像创建容器
[root@docker ~]# docker create ubuntu
eb1aa0ca86b2d49250ebe64913af50e88482ad68b9c3e61ef8f8da9c24b00f7a

# create 的容器状态为 Created
[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS    PORTS     NAMES
eb1aa0ca86b2   ubuntu    "/bin/bash"   2 seconds ago   Created              quizzical_goldwasser

# 启动容器
[root@docker ~]# docker start eb1aa0ca86b2
eb1aa0ca86b2

# 查看容器状态 ------ 启动了又退出了
[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS                     PORTS     NAMES
eb1aa0ca86b2   ubuntu    "/bin/bash"   23 seconds ago   Exited (0) 3 seconds ago             quizzical_goldwasser
示例:docker run 一步到位
bash 复制代码
[root@docker ~]# docker run ubuntu pwd
/

容器启动时执行 pwd,返回的 / 是容器中的当前目录。

执行 docker psdocker container ls 可以查看 Docker host 中当前运行的容器:

bash 复制代码
[root@docker ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

-a 参数可显示所有状态的容器(包括已退出的)。


2. 让容器长期运行

这种"一闪而过"的容器通常不是我们想要的结果。我们希望容器能够保持 running 状态,这样才能被我们使用。

核心原理:容器的生命周期依赖于启动时执行的命令,只要该命令不结束,容器也就不会退出。

理解了这个原理,我们就可以通过执行一个长期运行的命令来保持容器的运行状态:

bash 复制代码
[root@docker ~]# docker run ubuntu /bin/bash -c "while true ; do sleep 1 ; echo hahaha; done"

while 语句让 bash 不会退出。不过这种方法有个缺点:它占用了一个终端 。我们可以加上 -d 参数以后台方式启动容器:

bash 复制代码
[root@docker ~]# docker run -d ubuntu /bin/bash -c "while true ; do sleep 1 ; echo hahaha; done"
2a0bfa267fe146753b4fc8b23d55b08fbe3a5f9b5e093de6885133f6bbd20c56

容器启动后直接回到了 docker host 的终端。返回的长字符串即为容器的完整 ID。

容器标识方式
  • 长 ID:启动时返回的完整哈希值
  • 短 ID:长 ID 的前 12 个字符
  • 容器名称 :可通过 --name 参数显式命名,不指定则 Docker 自动分配
bash 复制代码
[root@docker ~]# docker stop 081be2bc2e1d
081be2bc2e1d
运行后台服务

容器更常见的用途是运行后台服务,例如 HTTP Server:

bash 复制代码
[root@docker ~]# docker run -d --name my_httpd httpd

通过 --name 指定了容器名称,容器运行命令为 httpd-foreground(由 CMD 指令指定)。


3. 两种进入容器的方法

我们经常需要进入容器内部进行工作,比如查看日志、调试、启动其他进程等。Docker 提供了两种方法:attachexec

docker attach

通过 docker attach 可以连接到容器启动命令的终端:

bash 复制代码
[root@docker ~]# docker run -d ubuntu /bin/bash -c "while true ; do sleep 1 ; echo I_am_in_container ; done"
dc508b94447f83b46080267580607569a187fcc7f780433f646e9d66949731a6

[root@docker ~]# docker attach dc508b94447f
I_am_in_container
I_am_in_container
I_am_in_container

提示 :可通过 Ctrl+P 然后 Ctrl+Q 组合键退出 attach 终端。

docker exec

通过 docker exec 进入容器:

bash 复制代码
[root@docker ~]# docker exec -it dc508b94447f bash
root@dc508b94447f:/#

参数说明:

  • -it:以交互模式打开 pseudo-TTY,执行 bash
  • 进入容器后,hostname 即为容器的短 ID
  • 可以像在普通 Linux 中一样执行命令
  • 执行 exit 退出容器,回到 docker host

docker exec -it <container> bash|sh 是进入容器最常用的方式。

attach 与 exec 的核心区别
对比维度 docker attach docker exec
终端类型 直接进入容器启动命令的终端 在容器中打开新终端
进程行为 不会启动新进程 可以启动新进程
适用场景 查看启动命令的输出 日常操作(推荐)

如果只是为了查看启动命令的输出,也可以使用 docker logs 命令:

bash 复制代码
[root@docker ~]# docker logs -f dc508b94447f
I_am_in_container
I_am_in_container

-f 的作用与 tail -f 类似,能够持续打印输出。


4. 运行容器的最佳实践

按用途划分,容器大致可分为两类:

服务类容器

以 daemon 形式运行,对外提供服务(如 Web Server、数据库等)。通过 -d 以后台方式启动是最佳选择。如需排查问题,可通过 exec -it 进入容器。

工具类容器

提供临时工作环境,通常以 run -it 方式运行:

bash 复制代码
[root@docker ~]# docker run -it busybox
/ # wget www.baidu.com
Connecting to www.baidu.com (223.109.82.6:80)
saving to 'index.html'
index.html           100% |********************|  2381  0:00:00 ETA
'index.html' saved
/ # exit

工具类容器多使用基础镜像,例如 busyboxdebianubuntu 等。

容器运行小结

生命周期管理

  1. 当 CMD / ENTRYPOINT / docker run 指定的命令运行结束时,容器停止
  2. 通过 -d 参数在后台启动容器
  3. 通过 exec -it 可进入容器并执行命令

容器标识方式

  1. 短 ID
  2. 长 ID
  3. 容器名称(通过 --name 命名,可通过 docker rename 重命名)

5. 容器常用操作

stop / start / restart

通过 docker stop 可以停止运行的容器。容器在 Docker host 中实际上是一个进程,docker stop 本质上是向该进程发送 SIGTERM 信号。

如果想快速停止容器,可使用 docker kill,其作用是向容器进程发送 SIGKILL 信号。

bash 复制代码
# 停止容器
[root@docker ~]# docker stop <container>

# 快速停止
[root@docker ~]# docker kill <container>

# 重新启动已停止的容器(保留首次启动的所有参数)
[root@docker ~]# docker start <container>

# 重启容器 = stop + start
[root@docker ~]# docker restart <container>
自动重启策略

容器可能因某种错误而停止运行。对于服务类容器,我们通常希望它能自动重启。启动容器时设置 --restart 即可:

bash 复制代码
# 无论何种原因退出(docker stop 除外),立即重启
[root@docker ~]# docker run -d --restart=always httpd

# 进程退出代码非 0 时重启,最多 3 次
[root@docker ~]# docker run -d --restart=on-failure:3 httpd

注意--restart=always 在容器因 docker stopdocker kill 退出时不会自动重启。

pause / unpause

当需要暂时让容器暂停工作(如打快照、释放 CPU 资源)时:

bash 复制代码
# 暂停容器(不占用 CPU 资源)
[root@docker ~]# docker pause <container>

# 恢复运行
[root@docker ~]# docker unpause <container>
删除容器

使用 Docker 一段时间后,host 上可能会积累大量已退出的容器,它们依然占用文件系统资源:

bash 复制代码
# 删除指定容器(可一次指定多个)
[root@docker ~]# docker rm 35af7150bd17 9769bb915803

# 批量删除所有已退出的容器
[root@docker ~]# docker rm -f $(docker ps -aq -f status=exited)

# ⚠️ 慎用:删除所有状态的容器
[root@docker ~]# docker rm -f $(docker ps -aq)

docker rm 删除容器,docker rmi 删除镜像,注意区分。


6. 一张图搞懂容器状态机

容器的各种状态之间的转换关系可以总结为如下状态机:

  • docker createCreated 状态
  • docker start → 以后台方式启动容器
  • docker run = docker create + docker start
  • docker stop → 发送 SIGTERM,容器进入 Exited 状态
  • docker kill → 发送 SIGKILL,容器快速退出
  • docker restart → stop + start
  • docker pause / docker unpausePausedRunning

关于 --restart 的重要补充 :只有当容器的启动进程退出 时(正常退出或 OOM 等非正常退出),--restart 才生效。如果容器是因为执行 docker stopdocker kill 退出,则不会自动重启。


7. 限制容器对内存的使用

cgroup 简介

Docker 通过 cgroup(Control Groups)来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面。

cgroup 是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源的机制,被 LXC、Docker 等很多项目用于实现进程资源控制。其核心子系统包括:

子系统 功能
blkio 限制每个块设备的 I/O 控制(磁盘、光盘、USB 等)
cpu 通过调度程序为 cgroup 任务提供 CPU 访问
cpuacct 产生 cgroup 任务的 CPU 资源报告
cpuset 为多核 CPU 的 cgroup 任务分配独立的 CPU 和内存
devices 允许或拒绝 cgroup 任务对设备的访问
freezer 暂停和恢复 cgroup 任务
memory 设置内存限制并产生内存资源报告
net_cls 标记网络包以供 cgroup 使用
ns 命名空间子系统
perf_event 监测属于特定 group 的所有线程
内存限额配置

与操作系统类似,容器可使用的内存包括物理内存swap。Docker 通过以下参数控制:

  • -m--memory:设置内存使用限额(如 100M、2G)
  • --memory-swap:设置 内存+swap 的使用限额
bash 复制代码
# 允许容器最多使用 200M 内存和 100M swap
[root@docker ~]# docker run -m 200M --memory-swap=300M ubuntu

--memory-swap 的几种情况

配置 含义
-m 300m --memory-swap=1g 可用 300M 物理内存 + 700M swap
不设置 --memory-swap swap 默认为 -m 值的两倍
--memory-swap 等于 -m 容器不能使用 swap
--memory-swap=-1 内存受限,swap 不受限制
压力测试实验

使用 stress 工具构建测试镜像:

bash 复制代码
# Dockerfile
FROM ubuntu
MAINTAINER gaoqd "6946630@qq.com"
RUN apt-get -y update && apt-get -y install stress
ENTRYPOINT ["/usr/bin/stress"]
bash 复制代码
# 构建镜像
[root@docker ~]# docker build -t ubuntu-with-stress .

测试内存限额:

bash 复制代码
# 分配 280M(在 300M 限额内)------ 正常工作
[root@docker ~]# docker run -it -m 200M --memory-swap=300M ubuntu-with-stress --vm 1 --vm-bytes 280M -v

# 分配超过限额 ------ stress 线程报错,容器退出
[root@docker ~]# docker run -it -m 200M --memory-swap=300M ubuntu-with-stress --vm 1 --vm-bytes 400M -v

8. 限制容器对 CPU 的使用

默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。

Docker 通过 -c--cpu-shares 设置容器使用 CPU 的权重,默认值为 1024

核心要点--cpu-shares 是一个弹性加权值,不能保证获得固定的 CPU 资源。只有在 CPU 资源紧张时,权重分配才会生效。

例如,两个容器 A(1024)和 B(512),当两者都需要 CPU 资源时,A 获得的 CPU 时间片是 B 的两倍。但如果 A 空闲,B 可以独占全部可用 CPU。

bash 复制代码
# 启动 container_A,cpu share 为 1024
[root@docker ~]# docker run --name "container_A" -it -c 1024 ubuntu-with-stress --cpu 4 -v

# 启动 container_B,cpu share 为 512
[root@docker ~]# docker run --name "container_B" -it -c 512 ubuntu-with-stress --cpu 4 -v

# 查看 cgroup 配置
[root@docker ~]# cat /sys/fs/cgroup/cpu/docker/<容器长ID>/cpu.shares
1024

# 实时监控资源使用
[root@docker ~]# docker stats

经验之谈--cpu 设置工作线程数量应等于 host 的 CPU 核数,才能将 CPU 压满,使权重比例清晰可见。


9. export 和 import 容器

docker export ------ 容器导出为 tar 包
bash 复制代码
# 创建测试容器
[root@docker ~]# docker run -d --name httpd1 httpd
e4f0a329c4df50ef0afb0bf21e22edc20e5a24c03ae64c6340aa2992d6e32525

# 导出容器(无论是否运行中)
[root@docker ~]# docker export -o myhttpd.tar httpd1
docker import ------ 将 tar 包导入为镜像
bash 复制代码
[root@docker ~]# docker import myhttpd.tar myweb:v1
sha256:9fcb90561d2014130ef19f9dd8af5efd7a0a4a2154907bc818ac6b887f13755c
docker save vs docker export 对比
对比维度 docker save docker export
操作对象 镜像 容器
元数据 保留所有元数据和历史记录 丢失所有元数据和历史记录
导入命令 docker load docker import
交叉兼容 ❌ export 的文件不能用 load 导入 ❌ save 的文件不能用 import 导入

10. 实现容器的底层技术

cgroupnamespace 是实现容器最关键的两种技术:cgroup 实现资源限额,namespace 实现资源隔离

cgroup 深入

cgroup 的配置实际存储在 /sys/fs/cgroup 目录中。启动容器时,Linux 会为每个容器创建一个以容器长 ID 命名的 cgroup 目录:

bash 复制代码
[root@docker ~]# docker run -it --cpu-shares 512 ubuntu-with-stress -c 1 -v

# 查看 cgroup 配置
[root@docker ~]# cat /sys/fs/cgroup/cpu/docker/<容器长ID>/cpu.shares
512
namespace ------ 资源隔离的基石

Linux 使用了六种 namespace,分别对应六种资源:

Namespace 隔离的资源
Mount 文件系统 ------ 容器拥有独立的文件系统视图
UTS hostname ------ 容器拥有自己的主机名
IPC 共享内存和信号量 ------ 独立的进程间通信
PID 进程 ID ------ 容器拥有独立的 PID 空间
Network 网卡、IP、路由 ------ 独立的网络栈
User 用户 ------ 容器可管理自己的用户,host 不可见

PID namespace 示例

bash 复制代码
[root@docker ~]# docker exec -it a80f1f8b692c bash
root@a80f1f8b692c:/# ps axf
  PID TTY      STAT   TIME COMMAND
   17 pts/1    Ss     0:00 bash
   25 pts/1    R+     0:00  \_ ps axf
    1 pts/0    Ss+    0:00 /bin/bash

容器内看到的 PID 与 host 中完全不同,PID=1 的进程也不是 host 的 init 进程。

User namespace 示例

bash 复制代码
# 在容器中创建用户
root@a80f1f8b692c:/# useradd gaoqd

# 回到 host,该用户并不存在
[root@docker ~]# su - gaoqd
su: user gaoqd does not exist

二、网络

1. Docker 网络概览

Docker 网络从覆盖范围可分为:

  • 单个 host 上的容器网络(本章重点)
  • 跨多个 host 的网络(进阶内容)

Docker 安装时会自动在 host 上创建三个网络:

bash 复制代码
[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
xxx            bridge    bridge    local
xxx            host      host      local
xxx            none      null      local

2. none 网络

none 网络的 driver 类型是 null,容器只有 lo 回环接口,无法与外界通信

bash 复制代码
[root@docker ~]# docker run -it --network=none busybox
/ # ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0

应用场景:封闭意味着隔离,对安全性要求高且不需要联网的应用可以使用 none 网络。例如,一个仅用于生成随机密码的容器,放到 none 网络中可避免密码被窃取。


3. host 网络

连接到 host 网络的容器共享 Docker host 的网络栈,网络配置与 host 完全一样。

bash 复制代码
[root@docker ~]# docker run -it --network=host busybox
/ # ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500
/ # hostname
docker

应用场景

  • 对网络传输效率有较高要求的容器
  • 需要直接配置 host 网络的容器(如跨 host 网络管理工具)

缺点:牺牲灵活性,需考虑端口冲突问题。


4. Bridge 网络(核心重点)

Docker 安装时会创建一个命名为 docker0Linux Bridge (网桥),可以理解为一个软件交换机。如果不指定 --network,创建的容器默认都会挂到 docker0 上。

网络架构原理

当创建一个 Docker 容器时,会同时创建一对 veth pair 接口:

  • 一端在容器内,即 eth0
  • 另一端在本地,挂载到 docker0 网桥,名称以 veth 开头
bash 复制代码
# 创建容器
[root@docker ~]# docker run -itd --name busybox1 busybox
5225d246f751

# 容器内查看网卡
[root@docker ~]# docker exec -it busybox1 sh
/ # ip a
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

# 宿主机上对应
# 25: vethddb2744@if24  ← 与容器 eth0 互为对端

veth pair 原理:一对成对出现的虚拟网卡,如同一根虚拟网线连接的两端。数据包发送到一个接口时,另一个接口也能收到相同的数据包。

Bridge 网络默认配置:

  • Subnet : 172.17.0.0/16
  • Gateway : 172.17.0.1(即 docker0)
bash 复制代码
[root@docker ~]# ip a | grep docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

5. 自定义容器网络

除了三个自动创建的网络,用户还可以根据业务需要创建 user-defined 网络。Docker 提供三种驱动:bridgeoverlay(跨主机)、macvlan(跨主机)。

创建自定义 bridge 网络
bash 复制代码
# 默认配置(自动分配网段)
[root@docker ~]# docker network create --driver bridge my_net
89f7bc11b602e84452ae01786113ac196a535f7296b9989ad941b3a48a5e6d04

# 指定网段和网关
[root@docker ~]# docker network create --driver bridge \
    --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
ec761bc51778f67c2245af72fd969f00cd517ee617a10a70dc01904f9c10279d
使用自定义网络
bash 复制代码
[root@docker ~]# docker run -it --network=my_net2 --name busybox2 busybox
/ # ip a
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500
    inet 172.22.16.2/24 brd 172.22.16.255 scope global eth0
指定静态 IP

注意 :只有使用 --subnet 创建的网络才能指定静态 IP。

bash 复制代码
[root@docker ~]# docker run -it --network=my_net2 --ip 172.22.16.100 --name busybox3 busybox

6. 容器之间的连通性

同网络容器互通:同一网络中的容器、网关之间可以直接通信。

跨网络隔离 :不同网桥上的网络默认不能通信 ,Docker 通过 iptablesDOCKER-ISOLATION 规则实现网络隔离。

即使 host 上已配置好路由表且启用了 ip forwarding,iptables 仍会 DROP 掉不同网桥之间双向的流量。

跨网络通信的解决方案

通过 docker network connect 为容器添加额外网卡:

bash 复制代码
[root@docker ~]# docker network connect my_net2 busybox1

这会在 busybox1 中新增一块 eth1 网卡,分配 my_net2 的 IP,从而实现跨网络通信。


7. 容器通信的三种方式

方式一:IP 通信

两个容器必须属于同一个网络 ,即可通过 IP 直接交互。在容器创建时通过 --network 指定网络,或通过 docker network connect 将现有容器加入指定网络。

方式二:Docker DNS Server

从 Docker 1.10 开始,docker daemon 实现了内嵌的 DNS Server,容器可以直接通过容器名通信:

bash 复制代码
[root@docker ~]# docker run -it --network my_net2 --name bbox1 busybox
[root@docker ~]# docker run -it --network my_net2 --name bbox2 busybox

# bbox2 可以直接 ping bbox1
/ # ping -c 3 bbox1
PING bbox1 (172.22.16.2): 56 data bytes
64 bytes from 172.22.16.2: seq=0 ttl=64 time=0.177 ms

限制 :Docker DNS 只能在 user-defined 网络中使用,默认的 bridge 网络不支持。

方式三:Joined 容器

Joined 容器可以使两个或多个容器共享一个网络栈 ,共享网卡和配置信息,通过 127.0.0.1 直接通信:

bash 复制代码
# 创建 httpd 容器
[root@docker ~]# docker run -d -it --name web1 httpd
f7641c43eb70

# 创建 busybox 并 join 到 web1 的网络
[root@docker ~]# docker run -it --network container:web1 busybox
/ # ip a
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN>
    inet 172.17.0.2/16    # 与 web1 完全相同的 IP 和 MAC

# 通过 loopback 访问 web1 的 HTTP 服务
/ # wget 127.0.0.1
saving to 'index.html'
'index.html' saved
/ # cat index.html
<html><body><h1>It works!</h1></body></html>

Joined 容器的典型场景

  1. 不同容器中的程序希望通过 loopback 高效快速通信(如 Web Server 与 App Server)
  2. 监控其他容器的网络流量(如独立的网络监控容器)

8. 容器如何访问外部世界

容器默认就能访问外网(这里的"外网"指容器网络以外的网络环境,并非特指 Internet)。

核心机制:NAT(网络地址转换)

Docker 通过 iptables 的 NAT 规则实现容器对外网的访问:

bash 复制代码
[root@docker ~]# iptables -t nat -S
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

NAT 转发流程

  1. 容器发送数据包:172.17.0.2 → 目标地址
  2. docker0 收到包,发现是外发包,交给 NAT 处理
  3. NAT 将源地址替换为 host 的 IP:192.168.108.30 → 目标地址
  4. 数据包从 ens160 发出,到达目标

可通过 tcpdump 抓包验证:

bash 复制代码
# 监控 docker0 接口
[root@docker ~]# tcpdump -i docker0 -n icmp
# 源地址为容器 IP:172.17.0.2

# 监控 ens160 接口
[root@docker ~]# tcpdump -i ens160 -n icmp
# 源地址已变为 host IP:192.168.108.30

9. 外部世界如何访问容器

答案:端口映射

Docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。

动态端口映射
bash 复制代码
[root@docker ~]# docker run -d -P httpd

# 查看映射端口
[root@docker ~]# docker port <container>
80/tcp -> 0.0.0.0:32768

# 访问容器服务
[root@docker ~]# curl 192.168.108.30:32768
<html><body><h1>It works!</h1></body></html>
指定端口映射
bash 复制代码
[root@docker ~]# docker run -d -p 8080:80 httpd

[root@docker ~]# curl 192.168.108.30:8080
<html><body><h1>It works!</h1></body></html>

端口映射的实现原理 :每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量。以 0.0.0.0:8080→80/tcp 为例:

  1. docker-proxy 监听 host 的 8080 端口
  2. 当请求到达 host_ip:8080 时,docker-proxy 转发给容器 172.17.0.3:80
  3. 容器中的服务响应请求并返回结果

三、总结

本文从 Docker 容器的基础操作出发,系统性地梳理了容器的生命周期管理、资源限制机制、底层实现技术以及完整的网络模型。容器技术的核心依赖两大 Linux 内核机制:cgroup 负责资源限额(CPU、内存、I/O),namespace 负责资源隔离(文件系统、网络、进程、用户等),两者协同工作使得容器既轻量又安全。在网络层面,Docker 提供了 none、host、bridge 三种原生网络驱动,以及灵活的自定义网络能力;容器间通信支持 IP、DNS 和 Joined 三种方式,对外通信则分别依赖 NAT 出站和端口映射入站。掌握这些底层原理,不仅能帮助我们更高效地使用 Docker,更能为后续学习 Kubernetes 等容器编排技术打下坚实基础。建议读者结合自身环境动手实践文中的每一个实验,唯有亲手操作,方能真正内化这些知识。

相关推荐
探索云原生1 小时前
KubeClipper 1.7.0 发布:Operation 优化与 Kubernetes 1.37 支持
linux·docker·云原生·kubernetes·go
fengkai45452 小时前
八、Docker详解1-3
运维·docker·容器
一技安身3 小时前
【信创】银河麒麟V10(ARM64,无GPU纯CPU)内网隔离离线部署Deepseek+openWebUI完整方案
docker
AI视觉网奇4 小时前
docker 迁移
docker
程序员老赵4 小时前
Docker 部署填鸭表单完整教程:搭建私有化问卷与表单收集平台
前端·docker·开源
MrSYJ5 小时前
Veth pair 细讲
docker·云原生·容器
普通人66 小时前
win系统docker运行
windows·docker
秦jh_6 小时前
【Docker】容器
运维·docker·容器
三8446 小时前
云安全 · 02 · 容器与 Docker 安全基础
web安全·docker·云安全