- 文档/教程
【免费下载链接】90DaysOfDevOps
This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.
导读
本指南对应 90DaysOfDevOps 学习路线中容器专题的第 47 天,主题是 Docker 网络与安全。此前几天的动手实验大多停留在"让容器跑起来"的层面,本篇文章将深入剖析容器背后的网络机制(默认 bridge 网络、端口映射与 NAT 转发),并系统讲解容器安全的最佳实践(非 root 用户、私有镜像仓库与精简镜像)。读完本文,你将掌握docker network系列命令的实际用法、桥接网络的工作原理,以及如何通过 Dockerfile 与运行参数把容器从"默认 root 权限"收敛到"最小权限",并能在 2022/Days/Containers 目录的配套示例中找到可直接复用的配置。
Docker 网络基础:docker network命令族
打开终端执行docker network,这是 Docker 中配置与管理容器网络的主命令。它暴露了一组子命令,覆盖了网络的全生命周期管理:
| 子命令 | 作用 |
|---|---|
docker network create | 创建新的自定义网络 |
docker network ls/list | 列出宿主机上已有的全部网络 |
docker network inspect | 查看某个网络的详细配置(ID、驱动、连接的容器等) |
docker network rm | 移除不再使用的网络 |
安装 Docker 之后,未做任何配置时,执行docker network ls会看到开箱即用的默认网络(对应截图 Day47_Containers2.png):
- 每个网络都有唯一的ID与NAME;
- 每个网络只关联一个 driver(驱动);
- 注意:
bridge网络与host网络恰好与它们各自的驱动同名,名字相同并不代表两者是同一个东西——网络是"实例",驱动是"实现",两者相关联但概念不同。
如果想深入了解某个网络,用docker network inspect bridge。返回的 JSON 中包含了名称、ID、驱动、已连接的容器以及更多配置细节(对应截图 Day47_Containers3.png)。
Docker Bridge 网络:单主机网络的实现基础
Docker Desktop 标准安装会提供一个预置网络bridge。从docker network ls的输出可以看到,名为bridge的网络由bridge驱动支撑,且作用域(Scope)为local。
这里有两个关键事实:
- bridge 驱动只提供单主机网络:
bridge网络仅存在于当前 Docker 宿主机上,使用 bridge 驱动的所有网络都是如此——它无法跨多台主机联通; - 底层实现是 Linux bridge(虚拟交换机):所有用 bridge 驱动创建的网络的底层都是 Linux 内核的 bridge 设备,可以把它理解为运行在 Linux 内核中的"虚拟交换机",负责在同一台宿主机内的容器之间转发二层数据帧。
连接容器到 bridge 网络
默认情况下,新建容器都会被挂到bridge网络,除非你在docker run时用--network显式指定其他网络。这正是默认网络如此重要的原因。
创建一个后台运行的 Ubuntu 容器:
docker run -dt ubuntu sleep infinitysleep infinity让容器始终在后台保持运行状态,方便我们后续进入容器内做实验。执行后终端会返回一串容器 ID(对应截图 Day47_Containers4.png)。
再次执行docker network inspect bridge,就会在 "Containers" 字段里看到刚才创建的容器——因为我们没有指定网络,它被自动接入了 bridge 网络。
进入容器内部一探究竟:
docker ps # 先拿到真实的容器 ID docker exec -it <容器ID> bash由于基础镜像非常精简,里面没有 ping 工具,需要先安装:
apt-get update && apt-get install -y iputils-ping ping -c5 www.90daysofdevops.com这能验证容器经由 bridge + 主机网络能否访问外网(对应截图 Day47_Containers6.png)。
实验结束后清理容器:
docker stop <容器ID>配置 NAT:通过端口映射对外提供服务
默认情况下,bridge 网络上的容器 IP 是 Docker 内部网段(例如 172.17.0.0/16),外部网络无法直接路由到容器 IP。要让外部流量到达容器内的服务,就需要端口映射,其本质是宿主机上的 NAT(网络地址转换):宿主机监听某个端口,把进入该端口的流量转发到容器内的目标端口。
下面启动一个官方 NGINX 容器,把宿主机的 8080 端口映射到容器内的 80 端口:
docker run --name web1 -d -p 8080:80 nginx首次运行时 Docker 会先从镜像仓库拉取 nginx 镜像(对应截图 Day47_Containers7.png)。
执行docker ps查看容器状态与端口映射(对应截图 Day47_Containers8.png):
CONTAINER ID IMAGE COMMAND PORTS NAMES ... nginx "/docker-entrypoint.…" 0.0.0.0:8080->80/tcp web1关键信息解读:
- 第一行即为新启动的
web1容器,它正在运行 NGINX 的入口脚本; - 端口映射
0.0.0.0:8080->80/tcp表示:宿主机所有网卡接口(0.0.0.0)上的 8080 端口,被转发到 web1 容器内的 80 端口; - 正是这条映射让容器的 Web 服务对外可达——外部访问者只需访问"Docker 宿主机的 IP:8080"。
接下来拿到宿主机真实 IP。在 WSL 终端里执行:
ip addr(对应截图 Day47_Containers9.png)记下宿主机的 IP 地址,然后在浏览器访问:
http://<宿主机IP>:8080/例如文档实验中的http://172.25.218.154:8080/(你的 IP 可能不同)。看到 NGINX 欢迎页即说明端口映射与 NAT 转发全部生效(对应截图 Day47_Containers10.png)。
这部分实验思路源自 2017 年 DockerCon 的官方网络演练,虽然年代久远,但 bridge 网络与端口映射的核心机制至今依然适用;原文演练的后半部分涉及 Docker Swarm,不属于本课主题,故不展开。
仓库中的端口映射实践
本仓库的多个 Compose 示例都是上述端口映射机制在真实应用中的落地,可对照阅读:
- elasticsearch-logstash-kibana/docker-compose.yml 通过
ports暴露了 Elasticsearch 的9200/9300、Logstash 的5000/5044/9600与 Kibana 的5601,并在文件末尾显式声明了networks: elastic: driver: bridge——即用 bridge 驱动构建一个名为elastic的自定义网络,把三个服务隔离在同一虚拟交换机上; - my_wordpress/docker-compose.yaml 中
ports: "8000:80"把宿主机 8000 端口映射到 WordPress 容器的 80 端口,与文中-p 8080:80的映射原理完全一致。
容器安全:从默认 root 走向最小权限
容器相比传统"整机"服务器,为工作负载提供了更安全的运行环境:它能把应用拆成更小、更松耦合的组件,彼此隔离,从而整体收窄攻击面。但这绝不意味着容器对恶意攻击者免疫——理解技术的安全缺陷并遵循最佳实践仍然必不可少。
告别 root 权限
到目前为止,我们部署的所有容器内部进程都是以root身份运行的,这意味着进程对容器以及宿主机环境拥有完整的管理权限。实验环境可以容忍这种临时行为,但生产环境绝不能如此。
两条改进路径:
路径一:在 Dockerfile 中创建非 root 用户(推荐)。仓库 2022/Days/Containers/Dockerfile 中保存着与本课完全一致的示例:
# Use the official Ubuntu 18.04 as base FROM ubuntu:18.04 # Install nginx and curl RUN apt-get update && apt-get upgrade -y #RUN apt-get install -y nginx curl #RUN rm -rf /var/lib/apt/lists/* RUN groupadd -g 1000 basicuser && useradd -r -u 1000 -g basicuser basicuser USER basicuser逐行解读:
FROM ubuntu:18.04:以官方 Ubuntu 18.04 为基础镜像;RUN apt-get update && apt-get upgrade -y:刷新软件源并升级基础软件包(原文档中 Dockerfile 里被注释掉的nginx curl安装行与rm -rf /var/lib/apt/lists/*清理行,正是"精简镜像"思路的体现,详见后文);RUN groupadd -g 1000 basicuser && useradd -r -u 1000 -g basicuser basicuser:创建 GID/UID 均为 1000 的系统用户basicuser及其同名用户组。指定固定 UID/GID 可以保证镜像内用户身份与宿主机命名空间可预期,便于卷权限管理;USER basicuser:此后的所有指令(以及容器运行时主进程)都以basicuser身份执行,不再使用 root。
路径二:用docker run --user运行时覆盖(仅作补充)。
docker run --user 1009 ubuntu--user会覆盖 Dockerfile 里指定的用户,容器将以 UID 1009 运行——只要该 UID 权限最低,就近似实现了最小权限原则。但文档明确指出:这种方式并未解决镜像本身底层的安全缺陷(镜像内若有以 root 身份执行的入口脚本,仍可能被利用)。因此更稳妥的做法是在 Dockerfile 中直接指定非 root 用户,让容器无论何时被启动都处于安全状态。
私有镜像仓库:把镜像供给收进自己手里
前几天的练习大量使用了 DockerHub 这样的公共镜像仓库。而由组织自建的私有镜像仓库(可在任意位置自托管,也可选用托管服务)能让团队完全掌控可用的镜像集合:
- 可控性:只有经过审核、签名的镜像才能进入私有仓库,避免从公共源拉取到来源不明的镜像;
- 信任模型:DockerHub 适合提供"基线"镜像,但它本质上是基础服务,你需要把大量信任押在镜像发布者身上;私有仓库则把信任边界收回到组织内部。
精简镜像:更小的镜像 = 更小的攻击面
镜像体积虽然与安全性不是直接强相关,却深刻影响攻击面:应用用不到的资源,就不应该出现在容器里。多余的库、工具和依赖越多,可被利用的潜在入口就越多。
文档特别提醒:盲目拉取latest标签容易带来大量"臃肿"内容(例如镜像里残留的 apt 缓存),这正是上节 Dockerfile 中注释掉rm -rf /var/lib/apt/lists/*清理行的原因所在。DockerHub 仓库页面会显示每个镜像的压缩后大小,本地则用docker images快速核对各镜像的体积(对应截图 Day47_Containers11.png)。建议生产镜像遵循"分层构建 + 清理中间产物 + 固定版本标签"的组合策略。
小结
Day 47 的核心收获可以归纳为三句话:
- 网络:
bridge是 Docker 默认的单主机网络,底层为 Linux bridge(虚拟交换机),所有未指定网络的容器默认接入其中; - 对外联通:bridge 网络内部 IP 不可被外部直接路由,必须借助
-p <主机端口>:<容器端口>的端口映射(NAT)把服务暴露出去; - 安全:坚持"最小权限"(Dockerfile 中
USER非 root 用户 +docker run --user兜底)、"镜像可控"(私有仓库)与"镜像精简"(按需裁剪、避免latest臃肿)三原则。
下一篇(Day 48)将继续推进 90DaysOfDevOps 容器专题的学习;文中涉及的 Dockerfile 与 Compose 网络配置均可直接在 2022/Days/Containers 目录中查看与复用。
- 文档/教程
【免费下载链接】90DaysOfDevOps
This repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.
相关推荐
90DaysOfDevOps 实战指南:Docker 网络与安全(Day 47)
90DaysOfDevOps 实战指南:Docker 网络与安全(Day 47) Docker 让我们能以一条命令快速拉起容器,但容器之间、容器与宿主机之间如何
文档/教程90DaysOfDevOps 实战:Docker 网络与容器安全加固(Day 47)
90DaysOfDevOps 实战:Docker 网络与容器安全加固(Day 47) 在 90DaysOfDevOps 为期 90 天的 DevOps 学习路线
文档/教程90DaysOfDevOps 第 47 天:Docker 网络与容器安全实战指南
90DaysOfDevOps 第 47 天:Docker 网络与容器安全实战指南 本篇文章来自 90DaysOfDevOps 学习项目 2022 年路线图的 D
文档/教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考