从虚拟机到容器:Docker镜像构建与Yum配置实战指南
上周在帮一个刚接触容器化的团队做环境迁移,他们之前习惯用虚拟机,现在要把几个老服务打包成 Docker 镜像。过程里遇到一个挺典型的问题:他们照着教程跑通了docker run,但一说到“自己做个镜像”,或者“在容器里装个软件、配个源”,就卡住了。不是命令敲错,就是镜像臃肿,要么就是容器里网络不通,Yum 仓库配不上。这其实不是个例。很多从虚拟机或物理机迁移过来的开发者,容易把容器当成一个“轻量级虚拟机”来用,在里面随意yum install,结果做出来的镜像动辄几个G,完全失去了容器的优势。Docker 的核心思想是“一次构建,处处运行”,而这个“构建”的过程,恰恰是区分“会用 Docker”和“理解 Docker”的关键。今天,我们就围绕“定制简单镜像”和“容器内配置”这两个核心动作,拆解一下背后的逻辑。你会发现,真正重要的不是那几个Dockerfile指令,而是理解镜像的分层存储、构建上下文、以及如何设计一个既符合需求又保持精简的镜像。我们以最经典的场景为例:为一个基于 CentOS 的容器配置 Yum 仓库,并在其中安装部署一个服务(比如 Nginx)。这个过程会清晰地展示,从“跑起来”到“用得好”,中间隔着哪些必须想清楚的细节。1. 先想清楚:我们到底在“构建”什么?在动手写Dockerfile之前,最容易踩的坑就是没想清楚最终产物的形态。你是在构建一个不可变的交付物,而不是一个可以随时 SSH 进去修修补补的环境。1.1 镜像不是虚拟机:理解分层与联合文件系统虚拟机的镜像通常是一个完整的磁盘快照,而 Docker 镜像是由一系列只读层(Layer)叠加起来的。每一层代表文件系统的一次修改(比如添加一个文件、执行一条命令)。当你运行一个容器时,Docker 会在这些只读层之上,添加一个可写的容器层。这种设计带来两个核心影响:构建可缓存:如果Dockerfile的某一层及之前的内容没有变化,Docker 在下次构建时会直接使用缓存,极大加快构建速度。镜像要精简:每一层都会占用空间,并且会一直存在于镜像历史中。一个糟糕的Dockerfile会产生大量中间层,导致镜像臃肿。所以,我们的目标不是“在容器里把环境配好”,而是“用一系列指令,描述如何从一个基础层开始,最终叠加出一个包含我们所需服务的只读文件系统”。1.2 构建上下文:那些被你无意中打包的“垃圾”另一个常见误区是docker build命令中的那个“.”。这个点代表构建上下文(Build Context),Docker 守护进程会把这个目录下的所有文件(受.dockerignore约束)打包发送给 Docker 引擎。如果你在项目根目录下执行docker build -t myapp .,而根目录里有node_modules、.git、日志文件等,这些都会被发送,导致构建过程缓慢,甚至因上下文过大而失败。一个清晰的意识是:Dockerfile里的COPY或ADD指令,其源路径是相对于构建上下文的,而不是你执行docker build命令的终端所在路径。2. 动手实践:从零构建一个带自定义 Yum 源的 Nginx 镜像让我们从一个具体的例子开始。目标是:基于 CentOS 7 官方镜像,配置国内阿里云的 Yum 仓库源,然后安装并配置 Nginx,最终制作成一个镜像。2.1 第一步:准备基础材料和编写 Dockerfile首先,创建一个干净的工作目录:mkdir docker-nginx-example cd docker-nginx-example在这个目录下,我们准备两个关键文件:一个Dockerfile,描述构建步骤。一个nginx.conf,用于覆盖默认的 Nginx 配置(可选,但更规范)。我们先创建最简单的Dockerfile: