Skip to content
 

搞懂 Docker 镜像与网络隔离:从底层原理到 Nginx 双层代理架构

更新: 8/1/2026字数: 0 字 时长: 0 分钟

在接触 Docker 的过程中,很多刚上手的开发者常常会被一系列概念绕晕:

  • Docker 镜像到底是个啥?它里面装了完整的操作系统吗?
  • 镜像里的 Nginx 和我宿主机上装的 Nginx 会冲突吗?
  • 为什么生产环境中经常在宿主机上跑一个 Nginx,又在 Docker 容器里跑一个 Nginx?流量到底是怎么传过去的?

今天新节就从镜像本质、系统隔离机制、网络映射原理三个维度,把 Docker 的核心逻辑一文彻底讲透。

一、 Docker 镜像的本质:它到底是个什么文件?

简而言之,Docker 镜像(Image)就是一个包含了应用程序及其完整运行环境的“只读打包文件”

如果你把一个 Docker 镜像导出(docker save)并解压,你会发现它的内部结构主要由 3 部分组成:

  1. 分层文件系统(Layers / Tarballs):按照 Dockerfile 的步骤,一层层叠加的只读压缩包。
  2. 配置文件(JSON):记录镜像的元数据、启动命令(CMD / ENTRYPOINT)和环境变量。
  3. 镜像间依赖与 Hash 校验:实现不同镜像间共同底座的复用(比如两个镜像都依赖 Ubuntu 基础层,本地只需要下载一份)。

💡 关键点: 镜像本身是 绝对只读(Read-Only) 的。当我们运行 docker run 启动容器时,Docker 引擎只是在镜像只读层的最上方,叠加了一层极薄的 “容器可写层(Writable Layer)”。你的改动和日志都写在这层,绝不会破坏原始镜像。

二、 镜像里包含了操作系统吗?

包含了,但只包含了一半。

操作系统在结构上可以拆分为:

  • 内核空间(Kernel Space):掌控硬件、调度 CPU 和内存,是操作系统的心脏。
  • 用户空间(User Space):包含 /bin/usr、包管理器(apt/yum)、C 语言基础库等外壳文件。

Docker 镜像剥离了内核,它打包的只是完整的“用户空间文件”。

text
┌──────────────────────────────────────────────────────────┐
│                   容器 (Container)                       │
│  ┌────────────────────────────────────────────────────┐  │
│  │ 镜像打包的用户空间 (User Space: apt, glibc, code)   │  │
│  └─────────────────────────┬──────────────────────────┘  │
└────────────────────────────┼─────────────────────────────┘
                             ▼ (系统调用 Direct System Calls)
┌──────────────────────────────────────────────────────────┐
│             宿主机共享内核 (Host Linux Kernel)             │
└──────────────────────────────────────────────────────────┘

这也解释了为什么 Docker 容器启动只需要毫秒级:因为它不需要经历载入 Kernel 的开机过程,直接共享并调用宿主机的内核运行进程。

三、 容器内的 Nginx 与宿主机 Nginx 的关系

答案是:没有任何直接关系,它们是两个彻底隔离的独立进程。

  • 文件隔离:宿主机的 /etc/nginx/ 和容器内的 /etc/nginx/ 处于完全不一样的文件系统,互不影响。
  • 进程隔离:它们使用不同的命名空间(Namespace),彼此感知不到对方的存在。
  • 版本隔离:宿主机可以跑 Nginx 1.18,容器里可以跑 Nginx 1.25,完全不会冲突。

它们唯一的“连接点”,只有宿主机的网络端口映射(Port Mapping)

关键对比:宿主机 Nginx vs 容器内 Nginx 配置差异

配置项宿主机 Nginx 配置容器内 Nginx 配置
listen 端口80443(暴露给公网)80(仅在容器内部或网桥中暴露)
server_name具体的公网域名(如 example.comlocalhost_(无需关注域名)
SSL 证书 (ssl_certificate)(配置公网 Let's Encrypt / 阿里云证书)(接收的是宿主机解密后的纯 HTTP)
proxy_pass 目标指向宿主机映射端口 [http://127.0.0.1:8001](http://127.0.0.1:8001)指向静态文件路径,或容器内后端 3000 端口

💡 总结一句话: 容器内的 Nginx 不需要知道自己叫什么域名,也不需要关心 HTTPS 证书。它只需要安静地监听自己的 80 端口,拿到宿主机丢进来的流量,然后去读本地静态文件或者丢给同容器的后端程序即可。

四、 工业界实战:Nginx 双层反向代理架构拆解

在实际生产环境中,我们经常采用 “宿主机总网关 Nginx + 容器子服务 Nginx” 的双层架构。

当用户访问 example.com 时,整个流量路径是如何穿透的呢?

text
[ 浏览器 / 用户 ]

      │ 1. 访问 example.com (通过 DNS 解析到宿主机公网 IP)

┌─────────────────────────────────────────────────────────────┐
│ 宿主机 (Host Machine)                                       │
│                                                             │
│   ┌─────────────────────────┐                               │
│   │  宿主机 Nginx 进程       │                              │
│   │  (监听 80 / 443 端口)   │                               │
│   └────────────┬────────────┘                               │
│                │ 2. 匹配 server_name 和 location            │
│                │    执行 proxy_pass http://127.0.0.1:8001;  │
│                ▼                                            │
│   ┌─────────────────────────┐                               │
│   │ 宿主机网络栈 / iptables  │                               │
│   │ (监听本地 8001 端口)    │                                │
│   └────────────┬────────────┘                               │
│                │ 3. Docker nat 表规则重定向 (DNAT)           │
│                │    将流量转投至容器内网 IP:80                │
└────────────────┼────────────────────────────────────────────┘

                 ▼ (通过 docker0 虚拟网桥)
┌─────────────────────────────────────────────────────────────┐
│ Docker 容器 (Container)                                     │
│                                                             │
│   ┌─────────────────────────┐                               │
│   │  容器内 Nginx 进程       │  4. 收到请求,返回响应内容     │
│   │  (监听容器内 80 端口)    │  ───────────────────────►     │
│   └─────────────────────────┘                               │
└─────────────────────────────────────────────────────────────┘

1. 宿主机 Nginx 负责什么?(入口大堂经理)

  • 域名路由:把 a.com 转发给容器 A(127.0.0.1:8001),把 b.com 转发给容器 B(127.0.0.1:8002),实现单台服务器部署多站点。
  • SSL 卸载:把公网 HTTPS 证书配置在宿主机。解密后,以纯 HTTP 明文发给内部容器,减轻容器负担。

宿主机核心配置:

在宿主机的 Nginx 配置文件(通常位于 /etc/nginx/conf.d/example.conf)中,反向代理的配置大致如下:

nginx
server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

    location / {
        # 将流量转发给容器映射在宿主机的 8001 端口
        proxy_pass http://127.0.0.1:8001;

        # 传递真实客户端 IP
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
    }
}

2. 容器内 Nginx 负责什么?(干活的员工)

因为宿主机已经把域名解析和 HTTPS 解密做完了,容器内的 Nginx 配置可以极度精简,它不需要关心证书,也不需要关心真实的域名。

容器内核心配置:

nginx
server {
    # 只需监听容器内部的 80 端口
    listen 80;
    server_name localhost;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html; # 支持前端 SPA 路由
    }
}

3. 底层关键技术:Docker 如何把 127.0.0.1:8001 接到容器里的?

你可能会问:“宿主机 Nginx 把请求发给了 127.0.0.1:8001,容器里的 Nginx 监听的不是容器内部的 80 端口吗?它们是怎么接通的?”

这里依靠的是 Docker 启动容器时的 -p 端口映射机制(如 docker run -d -p 8001:80 nginx):

  1. 宿主机建立端口监听: Docker 引擎启动时,会在宿主机上开启一个名为 docker-proxy 的用户态守护进程,专门监听宿主机的 8001 端口(绑定在 127.0.0.1:80010.0.0.0:8001)。
  2. Linux 内核层重定向(iptables / NAT): Docker 会自动在宿主机的 Linux 内核中写入一条 NAT 防火墙规则(iptables)。当宿主机的 Nginx 连接 127.0.0.1:8001 时,内核会自动把数据包的目标地址进行目标网络地址转换(DNAT),将目标改写成容器的虚拟内网 IP(例如 172.17.0.2:80)。
  3. 网桥转发(docker0): 数据包通过 Docker 创建的虚拟网桥(docker0)投递进容器内部,容器内的 Nginx 就顺利接收到了这个 HTTP 请求。

4. 为什么要这样“折腾”二次代理?(优势所在)

直接把容器的 80 端口映射到宿主机的 80 端口不就行了吗?为什么还要在宿主机上单独加一层 Nginx?

这种“宿主机总 Gateway + 容器子服务”的架构有几个极大的优势:

  • 多站点共用 80/443 端口:如果你这台服务器上有 3 个域名(a.comb.comc.com),它们分别对应 3 个不同的 Docker 容器。由于公网 80 端口只有一个,只能由宿主机 Nginx 统一接收,然后根据域名转发给不同的容器端口(如 800180028003)。
  • 统一管理 SSL 证书(HTTPS 卸载):你只需要把 HTTPS 证书配置在宿主机 Nginx 上。宿主机 Nginx 负责解密 HTTPS 流量,然后用普通的 HTTP 明文传给容器内的 Nginx/应用,容器内部不需要再配置繁琐的证书。
  • 动静分离与安全屏障:宿主机 Nginx 可以做防火墙、限流、防御 DDoS 攻击、全局日志记录,不让恶意请求直接触及内部容器。

💡 总结一句话: 宿主机 Nginx 收到请求后,就像一个中转前台,按照配置把请求重新打包发给本地的 8001 端口;而 Docker 引擎就像内部网关,通过 iptables 规则把 8001 端口的数据包准确送入容器的 80 端口中。

总结

搞懂了 Docker 的镜像本质和网络穿透逻辑,你就会发现:

  1. 镜像就是一个包含了用户空间文件与启动配置的只读压缩包
  2. 容器与宿主机隔离,宿主机没装某软件完全不影响容器运行。
  3. 通过 宿主机 Nginx(做入口分发与 SSL)+ 容器 Nginx(做应用解耦与静态资源托管),我们可以用极低维度的复杂度,搭建起一套安全、灵活、易于扩展的现代 Web 架构。