🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度
在容器化部署的实践中,我们常常会遇到一个场景:从官方仓库拉取的镜像过于“纯净”,缺少项目运行所需的特定软件包或配置。例如,一个基于 CentOS 的容器,默认的 Yum 源可能访问缓慢甚至无法连接,导致无法安装 vim 、 wget 或 nginx 等服务。手动进入每个容器配置既繁琐又难以固化,无法实现“一次构建,处处运行”的 Docker 哲学。
本文将系统性地解决这个问题,手把手带你完成从 定制一个包含自定义 Yum 源的简单镜像 ,到 在容器内部署一个 Nginx 服务 的全过程。无论你是刚接触 Docker 的新手,还是希望优化现有部署流程的开发者,都能通过本文获得一套可直接复用的实战方案。我们将重点拆解 Dockerfile 的编写、镜像构建、容器运行、服务验证以及背后的原理,确保每一步你都能理解“为什么这么做”。
1. 核心概念与问题背景
在深入操作之前,我们需要明确几个关键概念,这有助于理解我们整个实践的目标和意义。
1.1 Docker 镜像与容器
简单来说, Docker 镜像 是一个只读的模板,它包含了运行一个应用所需的所有内容:代码、运行时环境、库、环境变量和配置文件。你可以把它理解为一个应用程序的“安装包”或“快照”。
Docker 容器 则是镜像的一个运行实例。当你运行一个镜像时,Docker 会创建一个隔离的进程空间(容器),镜像中的文件系统会在容器内被加载。容器是轻量级、可移植的,并且彼此隔离。
我们的目标就是创建一个 定制化的镜像 ,这个镜像基于某个官方基础镜像(如 centos:7 ),但预置了我们需要的软件仓库(Yum源)和软件包(如 Nginx),这样基于此镜像启动的每一个容器,都天然具备了这些能力。
1.2 Yum 仓库与容器化困境
Yum(Yellowdog Updater, Modified)是 RHEL/CentOS/Fedora 等 Linux 发行版的包管理器。它通过配置的软件仓库(Repository)来查找、下载并安装软件。
在物理机或虚拟机上,我们通常会配置国内镜像源(如阿里云、清华源)来加速软件下载。但在 Docker 容器中,情况有所不同:
- 基础镜像最小化 :为了保持镜像小巧,官方基础镜像通常只包含最核心的系统文件,不包含完整的
yum配置或任何额外的软件源。 - 容器生命周期短暂 :容器可能随时被销毁和重建。在运行的容器内手动配置 Yum 源,这些更改会随着容器的消亡而丢失。
- 构建效率 :如果在每次构建镜像时都从国外源下载软件,速度会非常慢,且容易因网络问题失败。
因此, 在构建镜像的阶段就配置好高速、稳定的 Yum 源 ,是提升 Docker 使用体验和构建成功率的关键一步。
1.3 本文实践路径预览
我们将遵循以下路径,逐步完成目标:
- 环境准备 :确保 Docker 环境就绪。
- 编写 Dockerfile :创建构建镜像的“蓝图”,在其中完成更换 Yum 源、安装 Nginx 等操作。
- 构建镜像 :使用
docker build命令,将 Dockerfile 转化为一个可用的镜像。 - 运行容器 :基于定制镜像启动容器,并验证内部服务。
- 访问服务 :从宿主机访问容器内运行的 Nginx 服务。
- 问题排查与优化 :分析常见问题并提供最佳实践。
2. 环境准备与版本说明
在开始之前,请确保你的操作环境满足以下要求。本文的示例命令在主流 Linux 发行版和 macOS 上均适用,Windows 用户建议使用 WSL2 或 Docker Desktop。
- 操作系统 :Ubuntu 20.04/22.04, CentOS 7/8, macOS, 或 Windows 10/11 with WSL2。
- Docker 引擎 :版本 20.10.0 或更高。我们将使用 Docker CE(社区版)。
- 命令行终端 :Bash 或 Zsh。
检查 Docker 是否安装及版本: 打开终端,执行以下命令:
docker --version
如果显示类似 Docker version 24.0.7, build afdd53b 的信息,说明 Docker 已安装。如果未安装,请参考 Docker 官方文档或使用系统包管理器安装。
重要提示 :本文示例以 CentOS 7 作为基础镜像。如果你使用 Ubuntu 基础镜像,包管理器为 apt ,其配置思路类似,但具体命令和文件路径不同。选择 CentOS 是因为其企业级应用广泛,且 Yum 配置是常见痛点。
3. 实战:定制一个带国内 Yum 源和 Nginx 的镜像
这是本文的核心部分,我们将创建一个完整的项目目录,并编写所有必要的文件。
3.1 创建项目结构
首先,创建一个专门的项目目录,用于存放所有相关文件,保持环境整洁。
mkdir -p ~/docker-custom-image && cd ~/docker-custom-image
3.2 编写 Dockerfile
Dockerfile 是一个文本文件,包含了一系列用于构建镜像的指令。我们在项目根目录下创建它。
vim Dockerfile
# 或使用其他编辑器,如 nano Dockerfile
将以下内容复制到 Dockerfile 中:
# 文件:Dockerfile
# 使用 CentOS 7 的官方镜像作为构建基础
FROM centos:7
# 维护者信息(可选)
LABEL maintainer="your-email@example.com"
# 1. 备份原有的 Yum 源配置文件(可选,但是个好习惯)
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
# 2. 使用阿里云的 CentOS 7 Yum 源
# 注意:这里直接使用 `curl` 下载,但基础镜像可能没有 curl,所以先安装 curl
RUN yum install -y curl
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
# 3. 清理旧的 Yum 缓存并生成新缓存
# `yum makecache` 会从新配置的源下载元数据,加速后续安装
RUN yum clean all && yum makecache
# 4. 安装 Nginx 服务
# 先安装 EPEL 扩展仓库以获取 Nginx
RUN yum install -y epel-release
# 安装 Nginx
RUN yum install -y nginx
# 5. 暴露 Nginx 默认的 HTTP 端口 (80)
EXPOSE 80
# 6. 设置容器启动时执行的命令
# 以前台方式启动 Nginx
CMD ["nginx", "-g", "daemon off;"]
关键指令解析:
-
FROM centos:7:指定基础镜像。所有后续操作都基于这个镜像层进行。 -
RUN:在构建过程中执行 shell 命令。每一条RUN指令都会创建一个新的镜像层。 -
EXPOSE 80:声明容器运行时监听的端口。这只是一个元数据,方便使用者知道该映射哪个端口。 -
CMD [“nginx”, “-g”, “daemon off;”]:指定容器启动时默认运行的命令。“daemon off;”让 Nginx 在前台运行,这对于 Docker 容器是必须的,因为容器需要有一个持续运行的前台进程才能保持活动状态。
3.3 构建自定义镜像
在包含 Dockerfile 的目录下,执行构建命令。 -t 参数用于给镜像打标签,格式为 名称:标签 。
docker build -t my-centos-nginx:1.0 .
命令解释:
-
docker build:构建镜像的命令。 -
-t my-centos-nginx:1.0:指定镜像名称为my-centos-nginx,标签为1.0。 -
.:最后一个点表示当前目录是构建上下文(Dockerfile所在路径)。Docker 守护进程会将该目录下的所有文件(除非被.dockerignore排除)发送给构建进程。
构建过程输出示例:
Sending build context to Docker daemon 2.048kB
Step 1/9 : FROM centos:7
7: Pulling from library/centos
Digest: sha256:...
Status: Downloaded newer image for centos:7
---> xxxxxxx
Step 2/9 : LABEL maintainer="your-email@example.com"
---> Running in xxxxxxx
Removing intermediate container xxxxxxx
---> xxxxxxx
Step 3/9 : RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
---> Running in xxxxxxx
Removing intermediate container xxxxxxx
---> xxxxxxx
... (后续步骤)
Step 9/9 : CMD ["nginx", "-g", "daemon off;"]
---> Running in xxxxxxx
Removing intermediate container xxxxxxx
---> xxxxxxx
Successfully built xxxxxxx
Successfully tagged my-centos-nginx:1.0
构建成功后,可以使用 docker images 命令查看本地镜像列表,应该能看到 my-centos-nginx 。
docker images | grep my-centos-nginx
3.4 运行容器并验证内部环境
现在,我们基于刚刚构建的镜像启动一个容器,并进入容器内部验证我们的配置是否生效。
1. 启动一个交互式容器:
docker run -it --rm --name test-container my-centos-nginx:1.0 /bin/bash
-
-it:分配一个伪终端并保持标准输入打开,允许我们与容器交互。 -
--rm:容器退出后自动删除。非常适合临时测试。 -
--name test-container:给容器起个名字。 -
my-centos-nginx:1.0:指定使用的镜像。 -
/bin/bash:覆盖 Dockerfile 中的CMD,启动 bash shell 而不是 Nginx,方便我们检查。
2. 在容器内验证 Yum 源: 容器启动后,你会进入容器的 bash 命令行。执行以下命令:
# 查看 Yum 源配置文件
cat /etc/yum.repos.d/CentOS-Base.repo | head -20
你应该能看到文件中包含 mirrors.aliyun.com 的 URL,说明源已成功替换。
# 测试 Yum 缓存和安装(例如安装一个 vim 来测试)
yum makecache fast
yum install -y vim
如果安装 vim 速度很快且成功,说明 Yum 源配置正确且可用。
3. 验证 Nginx 是否安装:
# 检查 Nginx 二进制文件是否存在
which nginx
# 检查 Nginx 版本
nginx -v
如果显示 Nginx 版本信息,则安装成功。
4. 退出容器:
exit
由于我们使用了 --rm 参数,退出后容器会自动被删除。
3.5 以服务方式运行容器并访问
我们的最终目标是运行一个 Nginx 服务。现在,我们以后台守护进程的方式运行容器。
1. 运行容器并映射端口:
docker run -d -p 8080:80 --name my-nginx-server my-centos-nginx:1.0
-
-d:后台运行容器。 -
-p 8080:80:端口映射。将宿主机的8080端口映射到容器的80端口。 -
--name my-nginx-server:指定容器名称。
2. 检查容器运行状态:
docker ps
你应该能看到名为 my-nginx-server 的容器处于 Up 状态。
3. 访问 Nginx 服务: 打开你的浏览器,访问 http://localhost:8080 或 http://<你的服务器IP>:8080 。 如果看到 Nginx 的欢迎页面(“Welcome to nginx!”),恭喜你,一个由自定义镜像运行的容器服务已经成功部署!
4. 查看容器日志: 如果服务没有如期运行,查看日志是首要的排查手段。
docker logs my-nginx-server
5. 停止并清理容器: 测试完毕后,可以停止并删除容器。
docker stop my-nginx-server
docker rm my-nginx-server
# 如果想一并删除镜像,可以使用
# docker rmi my-centos-nginx:1.0
4. 深入理解:Dockerfile 优化与分层构建
上面的 Dockerfile 虽然能工作,但从 Docker 最佳实践来看,有优化空间。最关键的概念是 镜像分层 和 构建缓存 。
4.1 优化后的 Dockerfile
我们将优化点集中在合并 RUN 指令和清理无用文件上,以减少镜像层数和最终镜像大小。
# 文件:Dockerfile.optimized
FROM centos:7
LABEL maintainer="your-email@example.com"
# 一次性执行所有系统更新和软件安装,使用 && 连接命令,并用 \ 换行保持可读性
# 清理缓存也在同一层完成,避免中间层残留缓存文件增加镜像大小
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& yum install -y epel-release \
&& yum install -y nginx \
&& yum clean all \
&& rm -rf /var/cache/yum/*
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
优化点解析:
- 合并 RUN 指令 :将多个
RUN合并为一个,使用&&连接命令,并用反斜杠\换行。这减少了镜像的层数(Dockerfile 每一条指令都会产生一层)。层数越少,镜像构建、推送和拉取效率越高。 - 清理缓存 :在安装软件后立即执行
yum clean all和rm -rf /var/cache/yum/*,清除 Yum 下载的软件包缓存。这些缓存文件对于运行时毫无用处,只会增加镜像体积。 务必在同一个RUN指令中清理 ,如果分开到另一层RUN,删除操作只是标记文件在当前层不可见,底层文件依然存在,无法减小体积。
4.2 构建缓存机制
Docker 构建具有缓存机制。它会顺序执行 Dockerfile 中的指令,并将每层结果缓存。如果 Dockerfile 或构建上下文没有变化,后续构建会直接使用缓存,极大加速构建过程。
缓存失效规则:
- 一旦某条指令的内容发生变化(例如
RUN后面的命令字符串),从该指令开始的所有后续指令的缓存都会失效。 -
COPY或ADD指令中涉及的源文件内容发生变化,则该指令及其后续指令缓存失效。
因此,在编写 Dockerfile 时,一个重要的原则是: 将变化频率低的指令放在前面,变化频率高的指令(如复制应用代码)放在后面 。这样,当代码修改时,可以复用前面耗时的系统依赖安装层。
5. 常见问题与排查思路
在实际操作中,你可能会遇到以下问题。这里提供系统的排查思路。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
docker build 失败,错误提示 Failed to download CentOS-7.repo 或网络超时。 | 1. 宿主机网络问题。 2. 镜像源地址不可用或访问慢。 3. Docker 守护进程代理未配置。 | 1. 在宿主机尝试 curl http://mirrors.aliyun.com/repo/Centos-7.repo ,测试连通性。 2. 更换其他国内源,如清华源 ( https://mirrors.tuna.tsinghua.edu.cn/help/centos/ )。 3. 如果公司环境需要代理,需配置 Docker 守护进程的 HTTP_PROXY 。 |
容器启动后立即退出, docker ps -a 显示 Exited (0) 。 | 容器内没有持续运行的前台进程。Docker 容器要求至少有一个前台进程,否则会认为任务完成而退出。 | 1. 检查 Dockerfile 中的 CMD 或 ENTRYPOINT 指令是否正确。 2. 对于 Nginx,必须使用 nginx -g ‘daemon off;‘ 在前台运行。 3. 使用 docker logs <容器名> 查看应用自身的错误日志。 |
访问 localhost:8080 连接被拒绝。 | 1. 容器没有成功启动。 2. 端口映射错误。 3. 宿主机防火墙阻止了端口访问。 4. 容器内服务未监听在 0.0.0.0 。 | 1. docker ps 确认容器是否在运行 ( Up 状态)。 2. docker port <容器名> 检查端口映射是否正确。 3. 检查宿主机防火墙规则(如 firewall-cmd 或 ufw )。 4. 进入容器 ( docker exec ) 检查服务进程 (`ps aux |
yum install 速度依然很慢。 | 1. Dockerfile 中更换的源未生效(文件路径错误)。 2. 构建时使用了缓存,未执行换源步骤。 3. 源本身速度慢。 | 1. 进入构建中的临时容器或最终容器,检查 /etc/yum.repos.d/ 下的文件内容。 2. 构建时使用 --no-cache 参数: docker build --no-cache -t ... 。 3. 尝试其他国内镜像源。 |
| 镜像体积过大。 | 1. 每一层 RUN 指令产生了大量临时文件或缓存。 2. 安装了非必需的软件包。 | 1. 遵循“在同一层清理”原则,如 yum install && yum clean all 写在一行。 2. 使用更小的基础镜像,如 centos:7 的瘦身版或 alpine 。 3. 使用多阶段构建( multi-stage build )来分离构建环境和运行环境。 |
6. 最佳实践与工程建议
将 Docker 用于实际项目时,遵循以下最佳实践可以避免很多坑。
6.1 镜像构建最佳实践
- 使用
.dockerignore文件 :在构建上下文目录创建.dockerignore文件,排除不必要的文件(如.git,node_modules, 日志文件等),可以加速构建过程并避免敏感文件被打入镜像。# .dockerignore 示例 .git node_modules *.log Dockerfile README.md - 固定基础镜像版本 :不要使用
centos:latest这样的浮动标签。应使用具体版本,如centos:7.9.2009,以保证构建环境的一致性,避免因基础镜像更新导致意外行为。 - 多阶段构建 :对于需要编译的应用(如 Go, Java),使用多阶段构建。在第一阶段(构建阶段)安装编译器、下载依赖并编译;在第二阶段(运行阶段)仅复制编译好的二进制文件到一个小体积的基础镜像(如
alpine)中。这能极大减小最终镜像体积。 - 非 root 用户运行 :在 Dockerfile 中创建非 root 用户,并使用
USER指令指定容器内进程的运行用户,以增强安全性。RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser
6.2 容器运行与管理建议
- 资源限制 :使用
-m,--cpus等参数限制容器可使用的内存和 CPU 资源,防止单个容器耗尽宿主机资源。docker run -d -m 512m --cpus="1.5" --name my-app my-image:tag - 数据持久化 :容器内的数据是易失的。对于数据库、配置文件、日志等需要持久化的数据,务必使用 Docker 卷(
-v)或绑定挂载。docker run -d -v /host/path/nginx.conf:/etc/nginx/nginx.conf:ro -v nginx-log:/var/log/nginx ... - 使用 Docker Compose :当需要定义和运行多个容器(如 App + DB + Cache)时,使用
docker-compose.yml文件来管理服务、网络、卷,比手动运行多个docker run命令更清晰、高效。 - 日志管理 :配置 Docker 容器的日志驱动和轮转策略,避免日志占满磁盘。生产环境通常会将日志收集到 ELK 或 Loki 等集中式日志系统。
6.3 Yum 源配置的进阶思考
- 离线环境构建 :在内网或离线环境中,可以预先将 RPM 包和仓库元数据下载到本地,然后在 Dockerfile 中使用
ADD或COPY指令将其复制到镜像中,并配置本地 Yum 源。 - 使用公司私有仓库 :企业内通常会搭建私有的 Docker 镜像仓库和 Yum 仓库。在 Dockerfile 中,应指向这些内部仓库地址,以提高构建速度和安全性。
- 区分构建时与运行时源 :有些软件在构建时需要开发库(
-devel包),而运行时不需要。可以在一个RUN指令中安装所有构建依赖,并在同一层末尾卸载它们,但这需要精细控制。更优雅的方式是使用多阶段构建。
通过本文的实践,你不仅学会了如何定制一个包含自定义 Yum 源和 Nginx 的 Docker 镜像,更重要的是理解了 Dockerfile 的编写哲学、镜像分层原理以及容器化部署的完整流程。这套方法可以迁移到任何基于 RHEL 系的镜像定制中,无论是安装 Python、Java 环境,还是部署 MySQL、Redis 等服务,思路都是相通的。接下来,你可以尝试为你的具体应用编写 Dockerfile,将其容器化,并进一步学习 Docker 网络、存储和编排(如 Docker Compose、Kubernetes)的知识,从而真正掌握现代化应用部署的利器。如果在实践中遇到其他问题,多查阅官方文档和善用 docker logs 、 docker exec 等调试命令,大部分问题都能迎刃而解。
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度



362

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



