🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度
如果你已经掌握了 Docker 的基本命令,能够拉取镜像、运行容器,但每次进入容器,都感觉像进入了一个“毛坯房”——没有你熟悉的工具,网络不通,想装个软件都找不到源。于是,你开始手动 yum install ,却发现容器里连 yum 仓库都没配置,或者速度慢得让人绝望。最终,你可能会选择放弃,回到熟悉的物理机或虚拟机环境。
这恰恰是很多开发者从 Docker “入门”到“放弃”的关键一步。他们理解了容器是轻量级的,却没能跨越“如何高效地定制一个真正可用的容器环境”这道坎。今天这篇文章,就是要彻底解决这个问题。我们不只讲“如何做”,更要讲清楚“为什么这么做”,以及“怎么做才是最佳实践”。
本文将带你完成三个核心目标,它们环环相扣,是容器化实践的基础:
- 从零定制一个简单的 Docker 镜像 :不止于
Dockerfile的语法,而是理解镜像分层的原理,以及如何编写高效、可维护的构建指令。 - 在容器内部署 Yum 仓库 :解决容器内软件安装的“源头活水”问题,特别是针对国内网络环境的优化。
- 在定制好的容器环境中安装部署服务 :以一个具体的服务(如 Nginx)为例,完成从镜像构建、容器运行到服务验证的完整闭环。
通过这个流程,你将不再把容器看作一个黑盒,而是能随心所欲打造适合自己项目需求的“精装房”。
1. 这篇文章真正要解决的问题:从“运行容器”到“驾驭容器”
很多 Docker 教程止步于 docker run hello-world 或 docker run -it centos bash 。这造成了普遍的误解:容器用起来很简单。但一旦需要为容器安装软件、配置环境、部署应用,新手就会遇到一连串的“坑”:
- 网络隔离之痛 :干净的容器内部无法访问外部网络,导致
yum install或apt-get update失败。 - 镜像“毛坯”之困 :官方基础镜像(如
centos:7)通常只包含最核心的系统文件,缺少vim、wget、net-tools等常用工具,开发调试效率极低。 - 重复劳动之累 :每次启动新容器,都要重复执行一系列安装配置命令,无法固化成果。
- 仓库配置之惑 :即便网络通,默认的国外 Yum 源速度慢,需要替换为国内镜像源,但这个配置过程在容器中如何持久化?
本文的核心,就是系统性地解决这些问题。我们将通过一个清晰的路径: 定制镜像(固化基础环境) -> 配置仓库(解决安装源) -> 部署服务(实现应用功能) 。这不仅是一组操作命令,更是一种容器化思维: 任何需要在容器内手动进行的、重复性的配置,都应该尝试通过 Dockerfile 固化到镜像中 。
最终,你将得到一个包含优化后的 Yum 源、常用工具以及你所需服务的“即用型”镜像。无论是用于开发测试,还是作为更复杂应用的基础镜像,这个能力都至关重要。
2. 核心概念澄清:镜像、容器与仓库
在开始动手之前,确保我们对三个核心概念的理解在同一频道上:
- Docker 镜像 :一个 只读的模板 。它包含了运行某个软件所需的所有内容:代码、运行时、库、环境变量和配置文件。你可以把它理解为一个应用程序的“安装包”或“蓝图”。镜像是分层的,每一层代表 Dockerfile 中的一条指令。这种分层结构使得镜像非常轻量和高效。
- Docker 容器 :镜像的一个 运行实例 。当你从镜像创建并启动一个容器时,Docker 会在镜像的只读层之上,添加一个可写的“容器层”。所有对运行中容器的修改(如创建文件、安装软件)都发生在这个可写层。容器是轻量级、可移植的,并且与宿主机和其他容器隔离。
- Docker 仓库 :用于 存储和分发镜像 的集中位置。Docker Hub 是默认的公共仓库。你可以拉取(pull)公共镜像,也可以推送(push)自己的镜像到公共或私有仓库。
- Yum 仓库 :这里是 RPM 包的管理仓库 ,特指在 CentOS、RHEL、Fedora 等 Linux 发行版中使用的软件包管理器源。为容器配置 Yum 仓库,是为了让容器内部的
yum命令能够从一个快速、稳定的服务器获取并安装软件包。
它们之间的关系是 :你编写 Dockerfile (菜谱)来定义如何构建一个镜像(预制菜)。构建好的镜像可以上传到仓库(中央厨房)。当你需要运行应用时,就从仓库拉取镜像,并创建/启动一个容器(上菜)。在容器运行期间,你可以进入容器进行临时操作,但最佳实践是将所有环境依赖都通过 Dockerfile 做到镜像里。
3. 环境准备与前置条件
在开始构建和运行之前,请确保你的环境已就绪。
宿主机要求 :
- 操作系统 :任何支持 Docker 的 Linux 发行版(如 CentOS 7/8, Ubuntu 18.04+)、macOS 或 Windows 10/11(需启用 WSL2 或 Hyper-V)。本文以 CentOS 7.9 为例进行演示,原理通用于其他系统。
- Docker 引擎 :已正确安装并运行。你可以通过以下命令验证:
如果未安装,可以参考网络材料中的提示或 Docker 官方文档进行安装。一个关键的先决条件是 内核版本需高于 3.10 ,可使用docker --version docker infouname -r检查。
关键准备 :
- 网络连接 :确保宿主机可以访问互联网,以下载基础镜像和软件包。
- 权限 :通常需要
sudo权限或直接以root用户操作 Docker 命令。也可以将普通用户加入docker用户组(sudo usermod -aG docker $USER),然后 重新登录 生效。 - 文本编辑器 :准备一个你熟悉的编辑器(如
vim,nano,VSCode)来编写Dockerfile。
4. 实战第一步:编写 Dockerfile 定制简单镜像
我们将创建一个最简化的自定义镜像,基于 centos:7 ,并为其安装一些常用工具。
首先,创建一个专门的工作目录并进入:
mkdir -p ~/docker-demo && cd ~/docker-demo
在该目录下,创建名为 Dockerfile 的文件(注意大小写):
vim Dockerfile
将以下内容写入 Dockerfile :
# 使用 CentOS 7 官方镜像作为构建的基础
FROM centos:7
# 维护者信息(可选,已弃用但常用作注释)
LABEL maintainer="your-email@example.com"
# 设置容器内的工作目录,后续的 RUN、COPY、CMD 等命令会以此目录为当前目录
WORKDIR /app
# 1. 安装基础工具包
# yum install: 安装软件包
# -y: 对所有询问自动回答“yes”
# 这里安装了网络工具、进程查看工具、文本编辑器等
RUN yum install -y wget net-tools vim-enhanced tree telnet
# 2. 清理 yum 缓存,减小镜像体积(重要优化步骤)
# yum clean all: 清除所有缓存数据(元数据、包文件等)
RUN yum clean all && rm -rf /var/cache/yum
# 3. 声明容器运行时监听的端口(此处以 80 端口为例,仅为声明,实际映射在运行时决定)
EXPOSE 80
# 4. 设置容器启动时默认执行的命令
# 此处启动一个 bash shell,保持容器运行并允许交互
CMD ["/bin/bash"]
逐行解析与最佳实践 :
-
FROM: 必须 是Dockerfile的第一条有效指令(除了ARG)。它指定了构建的基石。选择合适且尽量小的官方基础镜像(如centos:7,alpine)是优化镜像大小的第一步。 -
LABEL:为镜像添加元数据。maintainer指令已废弃,但用LABEL标注作者信息仍是好习惯。 -
WORKDIR:设置工作目录。如果目录不存在,Docker 会自动创建。这比一直使用RUN cd /path && do_something更清晰。 -
RUN:在构建镜像时, 在容器内部 执行命令。每一条RUN指令都会创建一个新的镜像层。 最佳实践是将相关的RUN指令用&&连接起来,并用\换行保持可读性,最后清理缓存 ,以减少镜像层数和不必要的数据。我们这里分两步是为了逻辑清晰。 -
EXPOSE: 仅作声明 。它告诉用户这个镜像中的应用程序打算使用哪个端口。它不会自动完成端口映射,真正的映射需要在docker run时使用-p参数。 -
CMD:指定容器 启动时 默认执行的命令。一个Dockerfile中只能有一条CMD指令。如果docker run指定了其他命令,则会覆盖CMD。这里我们默认启动bash,方便交互。
5. 构建镜像并运行测试容器
现在,使用 docker build 命令来构建镜像。
-
构建镜像 :
# -t 参数为镜像打标签,格式为 name:tag # 最后的 `.` 表示构建上下文为当前目录,Dockerfile 也在此目录 docker build -t my-centos:1.0 .构建过程会逐条执行
Dockerfile中的指令,并输出日志。你会看到 Docker 拉取centos:7基础镜像(如果本地没有),然后执行RUN指令安装软件。 -
查看镜像 : 构建成功后,使用
docker images查看本地镜像列表,应该能看到my-centos镜像。docker images | grep my-centos -
运行测试容器 : 基于我们刚构建的镜像运行一个容器,并进入其 Shell 进行测试。
# -it 参数分配一个交互式终端并保持打开 # --rm 参数表示容器退出后自动删除,适合临时测试 # --name test-container 为容器指定一个名字,便于管理 docker run -it --rm --name test-container my-centos:1.0执行后,你会进入容器的 Bash Shell,提示符可能变为
[root@容器ID /app]#。 -
验证安装 : 在容器内,尝试使用我们安装的工具:
# 查看 IP 地址,验证 net-tools 已安装 ifconfig # 尝试使用 vim 编辑一个文件 vim --version | head -1 # 查看当前目录(应该是 /app,由 WORKDIR 设置) pwd # 退出容器(也会停止容器,由于用了 --rm,容器会被删除) exit如果这些命令都能成功执行,说明我们的第一步——定制一个包含基础工具的镜像——已经成功。
6. 核心痛点解决:为容器配置高效的 Yum 仓库
虽然我们的镜像里有了 yum ,但默认的仓库源可能速度很慢,尤其是在国内。我们需要在构建镜像时,就将其替换为国内的镜像源(如阿里云、清华源)。
原理 :CentOS 系统的 Yum 仓库配置位于 /etc/yum.repos.d/ 目录下,以 .repo 结尾。我们只需要在构建镜像时,用国内的源文件替换掉默认的源文件即可。
-
修改 Dockerfile : 我们更新之前的
Dockerfile,在安装软件 之前 ,先替换 Yum 源。这是更合理的顺序,能保证后续的yum install都使用高速源。FROM centos:7 LABEL maintainer="your-email@example.com" WORKDIR /app # === 新增步骤:备份并替换 Yum 源 === # 备份原有的仓库配置文件 RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup # 使用 curl 或 wget 下载阿里云的 CentOS 7 仓库配置文件 # 如果容器内没有 curl/wget,可以先安装,或者使用宿主机的文件复制(COPY指令) RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo # 清理旧的缓存并生成新缓存 RUN yum makecache # 安装基础工具包(现在会从阿里云源下载) RUN yum install -y wget net-tools vim-enhanced tree telnet # 清理缓存 RUN yum clean all && rm -rf /var/cache/yum EXPOSE 80 CMD ["/bin/bash"]方法二(推荐,更稳定) :使用
COPY指令将宿主机上预先下载好的.repo文件复制到镜像中。这样不依赖容器构建时的网络。- 首先,在宿主机工作目录 (
~/docker-demo) 下载阿里云的 repo 文件:wget -O CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo - 然后,修改
Dockerfile:FROM centos:7 LABEL maintainer="your-email@example.com" WORKDIR /app # === 使用 COPY 指令替换 Yum 源 === # 将宿主机当前目录下的 CentOS-Base.repo 文件复制到容器内的指定位置 COPY CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo # 安装基础工具包 RUN yum install -y wget net-tools vim-enhanced tree telnet && \ yum clean all && \ rm -rf /var/cache/yum EXPOSE 80 CMD ["/bin/bash"]
这种方法更优,因为文件是构建上下文的一部分,构建过程可重复,不依赖外部网络。
- 首先,在宿主机工作目录 (
-
重新构建镜像 :
# 给新镜像打上新的标签,如 2.0 docker build -t my-centos:2.0 . -
验证仓库配置 : 运行新镜像的容器,并检查仓库配置和安装速度。
docker run -it --rm my-centos:2.0在容器内执行:
# 查看仓库配置 cat /etc/yum.repos.d/CentOS-Base.repo | head -5 # 尝试搜索一个软件包,感受速度 yum search iftop你应该能看到仓库 URL 已经指向
mirrors.aliyun.com。
7. 在容器内安装并部署一个服务:以 Nginx 为例
现在,我们有一个“精装”的基础镜像。接下来,我们要在这个镜像里安装一个具体的服务——Nginx,并将其配置为容器的主进程。
目标 :构建一个启动后直接运行 Nginx 的镜像。
-
创建新的 Dockerfile : 我们基于
my-centos:2.0继续构建,或者从头开始。这里我们创建一个新的Dockerfile.nginx以示区别。cd ~/docker-demo vim Dockerfile.nginx写入以下内容:
# 基于我们之前优化过的镜像,也可以直接用 centos:7 并包含换源步骤 FROM centos:7 WORKDIR /app # 1. 配置国内 Yum 源(使用 COPY 方式,需确保 CentOS-Base.repo 文件存在) COPY CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo # 2. 安装 Nginx 和必要的工具 # CentOS 7 默认仓库的 Nginx 版本可能较老,我们直接安装 RUN yum install -y nginx && \ yum clean all && \ rm -rf /var/cache/yum # 3. 创建一个简单的自定义首页,覆盖 Nginx 默认页面 RUN echo "<h1>Hello, Docker & Nginx from Custom Image!</h1><p>This page is served from a container.</p>" > /usr/share/nginx/html/index.html # 4. 暴露 Nginx 默认的 HTTP 端口 EXPOSE 80 # 5. 设置容器启动命令:以前台方式启动 Nginx # -g 'daemon off;' 是让 Nginx 在前台运行的关键参数,防止容器启动后立即退出 CMD ["nginx", "-g", "daemon off;"]关键点解析 :
-
RUN yum install -y nginx:从我们配置好的国内源安装 Nginx,速度会快很多。 -
echo ... > index.html:这是一个简单的演示,修改了 Nginx 的默认首页。在实际项目中,你可能会用COPY指令将整个网站静态文件复制到容器内。 -
CMD ["nginx", "-g", "daemon off;"]:这是 最重要的一行 。默认情况下,Nginx 会以守护进程(daemon)模式启动,这种模式会启动主进程后立即退出,导致 Docker 容器认为主进程结束而停止运行。-g 'daemon off;'这个参数强制 Nginx 运行在前台,保持容器持续运行。
-
-
构建 Nginx 镜像 :
docker build -f Dockerfile.nginx -t my-nginx:1.0 .-f参数指定使用哪个 Dockerfile 文件。 -
运行 Nginx 容器 :
# -d: 后台运行容器 # -p 8080:80: 将宿主机的 8080 端口映射到容器的 80 端口 # --name my-web: 为容器命名 docker run -d -p 8080:80 --name my-web my-nginx:1.0 -
验证服务 :
- 查看容器状态 :
应该能看到docker ps | grep my-webmy-web容器处于Up状态。 - 查看容器日志 :
应该能看到 Nginx 启动成功的日志。docker logs my-web - 访问服务 : 打开你的浏览器,访问
http://你的宿主机IP:8080。你应该能看到我们自定义的 “Hello, Docker & Nginx from Custom Image!” 页面。 - 进入容器检查 (可选):
在容器内,你可以检查 Nginx 进程、配置文件等。docker exec -it my-web /bin/bashps aux | grep nginx cat /usr/share/nginx/html/index.html exit
- 查看容器状态 :
8. 完整流程回顾与高级技巧
至此,我们已经完成了“定制镜像 -> 配置仓库 -> 部署服务”的完整闭环。让我们梳理一下这个流程,并补充一些高级技巧和最佳实践。
完整工作流 :
- 规划 :明确镜像需要包含什么(基础系统、工具、软件、配置、文件)。
- 编写 Dockerfile :按照依赖顺序编写指令(基础镜像 -> 系统配置 -> 安装依赖 -> 复制代码 -> 设置启动命令)。
- 准备构建上下文 :将 Dockerfile 和需要复制到镜像的文件(如
.repo文件、应用代码)放在同一个目录。 - 构建镜像 :
docker build -t name:tag . - 运行测试 :
docker run -it --rm name:tag [command]进行交互式测试。 - 运行服务容器 :
docker run -d -p host_port:container_port --name container_name name:tag - 验证与维护 :通过
docker logs,docker exec, 浏览器访问等方式验证服务。
高级技巧与最佳实践 :
-
优化镜像体积 :
- 合并 RUN 指令 :如
RUN apt-get update && apt-get install -y package && rm -rf /var/lib/apt/lists/*。减少镜像层数。 - 使用
.dockerignore文件 :在构建上下文目录创建.dockerignore,排除不需要的文件(如.git,node_modules, 日志文件),避免它们被发送到 Docker 守护进程,加速构建。 - 选择更小的基础镜像 :考虑使用
alpine版本(如nginx:alpine,python:3.9-alpine),它们基于 Alpine Linux,体积极小。 - 多阶段构建 :对于需要编译的应用,在一个阶段编译,在另一个只包含运行环境的阶段复制编译结果,丢弃庞大的编译工具和中间文件。
- 合并 RUN 指令 :如
-
有效管理配置 :
- 环境变量 :使用
ENV指令设置环境变量,在容器内和 Dockerfile 后续指令中可用。敏感信息不应硬编码在镜像中。 - 配置文件挂载 :对于需要频繁修改的配置文件(如 Nginx 的
nginx.conf),使用-v参数将宿主机的目录或文件挂载到容器内,实现配置与镜像分离。docker run -d -p 80:80 -v /host/path/nginx.conf:/etc/nginx/nginx.conf:ro nginx
- 环境变量 :使用
-
Dockerfile 指令最佳实践 :
-
COPYvsADD:优先使用COPY,它更透明。ADD有额外的功能(解压、远程 URL),但行为不够清晰。 -
CMDvsENTRYPOINT:理解它们的区别。ENTRYPOINT定义容器的主程序,CMD提供默认参数。组合使用可以创建像可执行文件一样的镜像。 -
USER:如果应用不需要 root 权限,应使用USER指令切换到非 root 用户,增强安全性。
-
9. 常见问题与排查思路
在实践过程中,你可能会遇到以下问题。这里提供快速的排查指南。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
docker build 失败,提示 yum install 超时或无法下载。 | 1. 容器构建时无法访问网络。 2. 未配置或错误配置国内 Yum 源。 | 1. 检查宿主机网络。 2. 进入临时容器 docker run -it centos:7 bash ,尝试 curl -I mirrors.aliyun.com 。 3. 检查 Dockerfile 中 COPY 或下载 repo 文件的指令是否正确。 | 1. 确保使用 COPY 方式替换 repo 文件。 2. 检查 CentOS-Base.repo 文件内容是否正确。 3. 在 RUN yum install 前执行 yum makecache 。 |
docker run -d 后容器立即退出(Exited)。 | 1. 容器内没有前台进程在运行。 2. CMD 或 ENTRYPOINT 指定的命令执行完就结束了(如 bash )。 | docker logs <container_id> 查看容器日志。 docker run -it <image> 交互式运行看输出。 | 确保启动命令是长期运行的前台进程。对于 Nginx/Apache,使用 -g ‘daemon off;‘ 。对于 Web 应用,确保监听正确端口且在前台运行。 |
宿主机无法通过 -p 映射的端口访问容器服务。 | 1. 端口映射错误(主机端口被占用)。 2. 容器内服务未监听在 0.0.0.0 或对应端口。 3. 宿主机防火墙/安全组规则阻止。 | 1. docker ps 查看映射关系。 2. docker exec <container> netstat -tlnp 查看容器内监听端口。 3. curl localhost:container_port 在宿主机内测试容器端口。 | 1. 更换主机端口。 2. 确保应用配置监听 0.0.0.0 。 3. 调整防火墙规则(如 firewall-cmd )。 |
| 容器内修改了文件(如安装了新软件),但重启容器后修改丢失。 | 对容器的修改保存在可写层,容器删除后即丢失。这是容器的特性。 | 理解容器层与镜像层的区别。 | 任何需要持久化的配置或数据,都应通过 Dockerfile 固化到镜像中,或使用 -v 挂载数据卷/宿主机目录。 |
| 镜像体积过大。 | 1. 基础镜像过大。 2. 构建过程中产生了大量缓存和临时文件未清理。 | docker images 查看大小。 docker history <image> 查看各层大小。 | 1. 使用更小基础镜像(Alpine)。 2. 合并 RUN 指令,并在同一层清理缓存( apt-get clean , yum clean all , rm -rf /var/cache/* )。 3. 使用多阶段构建。 |
10. 总结与后续方向
通过本文的实践,你已经掌握了 Docker 容器化中最核心的主动技能: 定制镜像 。你不再只是被动地使用别人准备好的镜像,而是能够根据项目需求,从基础系统开始,配置环境、安装软件、部署应用,最终打包成一个可随时分发和运行的标准化单元。
回顾一下关键收获:
- Dockerfile 是镜像的蓝图 :每一行指令都对应镜像的一层,合理的指令顺序和合并能优化构建效率和镜像大小。
- 配置仓库是基础 :在构建阶段就为容器配置高速的软件源,是保证后续安装步骤成功和高效的前提。
COPY本地文件是最可靠的方式。 - 前台进程是容器的生命线 :容器内必须有一个前台进程持续运行,否则容器会退出。这是
CMD [“nginx”, “-g”, “daemon off;“]这类命令存在的根本原因。 - 端口映射连接内外 :
-p参数将容器内部端口暴露给宿主机,这是外部访问容器服务的桥梁。 - 数据持久化靠挂载 :容器的文件系统是临时的,重要数据和应用日志应通过
-v挂载到宿主机或数据卷。
接下来你可以探索的方向 :
- Docker Compose :当你需要定义和运行多个容器(如一个 Web 应用需要 Nginx、Python、Redis、MySQL)时,使用
docker-compose.yml文件来编排它们,一键启动整个应用栈。 - 私有镜像仓库 :搭建自己的 Docker Registry(如 Harbor),用于存储团队内部构建的镜像,实现安全的镜像分发和管理。
- 容器化完整应用 :尝试将一个你熟悉的传统应用(如一个 Spring Boot 或 Django 项目)容器化。思考如何管理配置文件、数据库迁移、日志收集等。
- 学习 Kubernetes :当容器数量增多,需要自动化部署、扩缩容、服务发现和负载均衡时,Kubernetes 是业界标准的选择。
建议你将本文中构建的 Dockerfile 保存下来,作为未来项目的一个模板。容器化的道路,从此由你自定义。
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度


被折叠的 条评论
为什么被折叠?



