🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度
在容器化部署的实践中,很多开发者会遇到一个典型场景:基于一个基础镜像启动容器后,发现里面缺少必要的软件包管理工具(如 yum )或特定的软件仓库,导致无法直接安装所需的服务。手动进入容器配置不仅繁琐,而且无法固化到镜像中,每次创建新容器都需要重复操作。本文将系统性地解决这个问题,手把手教你如何从零开始定制一个包含自定义 Yum 仓库的 Docker 镜像,并在此镜像基础上,完成一个服务(以 Nginx 为例)的安装与部署。整个过程将覆盖 Dockerfile 编写、镜像构建、仓库配置、服务安装、容器运行等核心环节,最终你将得到一个可复用的、开箱即用的服务镜像。
1. 核心概念与准备工作
在开始动手之前,我们需要明确几个核心概念,并准备好实验环境。
1.1 Docker 镜像与容器
Docker 镜像是用于创建 Docker 容器的只读模板。它包含了运行某个应用所需的所有内容:代码、运行时环境、库、环境变量和配置文件。你可以把它理解为一个应用程序的“安装包”或“快照”。
Docker 容器则是镜像的运行实例。你可以从同一个镜像启动多个容器,每个容器都是独立、安全的进程。容器与宿主机共享内核,但拥有独立的文件系统、网络和进程空间。
我们本次的目标,就是创建一个 自定义的镜像 ,这个镜像不仅包含基础的操作系统,还预配置好了我们需要的软件源(Yum 仓库),并且预装了特定的服务。
1.2 Yum 仓库简介
Yum(Yellowdog Updater, Modified)是 RHEL、CentOS、Fedora 等 Linux 发行版上默认的包管理器。它通过访问配置好的软件仓库(Repository)来搜索、下载、安装、更新和删除软件包。
默认情况下,Docker 官方提供的 CentOS 等基础镜像,其内部的 Yum 仓库配置指向的是国外的官方源,在国内访问速度可能很慢甚至无法访问。因此,为容器内的 Yum 配置一个高速、稳定的国内镜像源(如阿里云、清华源)是提升构建效率的关键一步。
1.3 环境与工具准备
为了完成本教程,你需要准备以下环境:
- 操作系统 :任何支持 Docker 的 Linux 发行版(如 Ubuntu 20.04/22.04, CentOS 7/8)、macOS 或 Windows 10/11(需启用 WSL2)。本文命令以 Linux/macOS 的 Bash 环境为例。
- Docker 引擎 :确保已安装并运行 Docker。你可以通过以下命令验证:
如果未安装,请参考 Docker 官方文档或使用系统包管理器安装。对于生产环境,不建议使用一键安装脚本,应遵循官方的最佳实践进行安装。docker --version docker info - 文本编辑器 :用于编写 Dockerfile 和配置文件,如 Vim、VS Code、Nano 等。
- 网络连接 :需要能够访问互联网以下载基础镜像和软件包。
2. 定制基础镜像:编写第一个 Dockerfile
我们将从一个最基础的 CentOS 镜像开始,逐步添加我们的定制内容。
2.1 创建项目目录
首先,创建一个清晰的工作目录来管理所有相关文件。
mkdir -p ~/docker-custom-image && cd ~/docker-custom-image
2.2 编写 Dockerfile
Dockerfile 是一个文本文件,其中包含了一系列指令,用于指导 Docker 如何自动构建镜像。
在项目根目录下创建名为 Dockerfile 的文件:
vim Dockerfile
然后输入以下内容:
# 使用官方 CentOS 7 作为基础镜像
# 注意:CentOS 8 已停止维护,CentOS 7 也即将结束生命周期,生产环境请考虑 Rocky Linux/AlmaLinux 或 Ubuntu LTS。
# 这里使用 CentOS 7 仅作演示。
FROM centos:7
# 维护者信息(非必需,但建议保留)
LABEL maintainer="your-email@example.com"
# 设置工作目录,后续的 RUN、COPY、CMD 等指令会以此目录为当前目录
WORKDIR /root
# 1. 备份原有的 Yum 仓库配置文件
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
# 2. 使用阿里云的 CentOS 7 镜像源替换默认源
# 这里我们直接使用 curl 下载仓库配置文件。如果基础镜像没有 curl,可以先安装。
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
# 3. 清理旧的 Yum 缓存并生成新缓存
# 这能确保系统使用新的镜像源列表
RUN yum clean all && yum makecache
# 4. (可选)安装一些常用的工具,如 vim, wget, net-tools 等
RUN yum install -y vim wget net-tools telnet
# 5. 声明容器运行时监听的端口(例如 Nginx 的 80 端口)
# 这只是一个声明,方便使用者知道这个镜像会使用哪些端口。
EXPOSE 80
# 6. 设置容器启动时默认执行的命令
# 这里我们先启动一个 bash shell,方便交互式检查。
CMD ["/bin/bash"]
关键指令解释:
-
FROM: 指定基础镜像,所有构建都基于此镜像。 -
LABEL: 为镜像添加元数据,如维护者信息。 -
WORKDIR: 设置工作目录,类似cd命令。 -
RUN: 在构建镜像时执行的命令。每一条RUN指令都会创建一个新的镜像层。 -
curl -o ...: 下载文件并保存到指定路径。 -
yum clean all && yum makecache: 这是一个经典组合。clean all清除所有缓存,makecache根据当前仓库配置生成新的缓存,确保后续安装使用新源。 -
EXPOSE: 声明容器打算使用的端口,这只是一个文档性质的声明,不会自动映射到宿主机。 -
CMD: 指定容器启动时默认运行的命令。一个 Dockerfile 中只能有一个CMD指令(如果多个,只有最后一个生效)。这里我们让容器启动后进入 Bash,方便我们后续操作。
2.3 构建自定义基础镜像
在 Dockerfile 所在目录执行构建命令:
docker build -t my-centos:7-with-repo .
-
-t my-centos:7-with-repo: 为构建的镜像打上标签,格式为名称:标签。这里我们命名为my-centos,标签为7-with-repo。 -
.: 指定构建上下文路径为当前目录,Docker 引擎会将该目录下的所有文件发送给 Docker 守护进程。Dockerfile也默认从这个路径寻找。
构建过程会依次执行 Dockerfile 中的指令,下载基础镜像、执行 RUN 命令等。第一次构建可能会花费一些时间下载 centos:7 基础镜像。
构建成功后,可以使用 docker images 命令查看本地镜像列表,应该能看到新构建的 my-centos:7-with-repo 镜像。
docker images | grep my-centos
2.4 验证镜像与仓库配置
运行一个基于新镜像的容器,并检查 Yum 仓库是否已成功替换为阿里云源。
# 以交互模式运行容器,并分配一个伪终端(-it)
docker run -it --rm --name test-repo my-centos:7-with-repo
容器启动后,你会进入容器的 Bash Shell。执行以下命令验证:
# 查看 Yum 仓库列表
yum repolist
# 或者查看具体的仓库配置文件内容
cat /etc/yum.repos.d/CentOS-Base.repo | head -20
你应该能看到仓库的 baseurl 指向 mirrors.aliyun.com 。同时,尝试安装一个软件(比如之前没安装的 tree )来测试仓库是否可用:
yum install -y tree
tree --version
如果安装成功,说明我们的自定义基础镜像(包含国内 Yum 源)已经制作完成。输入 exit 退出容器。由于我们使用了 --rm 参数,退出后容器会自动删除,但镜像保留。
3. 在容器内安装与部署服务(以 Nginx 为例)
现在,我们有了一个“好用”的基础镜像。接下来,我们要在这个镜像的基础上,安装并配置一个具体的服务——Nginx Web 服务器,并制作成最终的应用镜像。
3.1 创建服务部署的 Dockerfile
我们不再修改之前的 Dockerfile ,而是采用更清晰的方式:基于我们刚构建的 my-centos:7-with-repo 镜像,创建一个新的、专门用于 Nginx 服务的 Dockerfile。
在项目目录下创建一个新的子目录和 Dockerfile:
mkdir nginx-service && cd nginx-service
vim Dockerfile
输入以下内容:
# 第二阶段:基于我们自定义的、带仓库的基础镜像
FROM my-centos:7-with-repo
# 维护者信息
LABEL maintainer="your-email@example.com"
# 设置工作目录(非必须,但好习惯)
WORKDIR /usr/share/nginx/html
# 1. 安装 Nginx
# 由于基础镜像已配置好阿里云源,这里安装会很快
RUN yum install -y nginx
# 2. 创建一个简单的自定义首页,覆盖 Nginx 默认页面
RUN echo "<h1>Hello from Custom Docker Image with Nginx!</h1><p>This page is served from a container with custom Yum repo.</p>" > /usr/share/nginx/html/index.html
# 3. 暴露 Nginx 默认的 HTTP 端口
EXPOSE 80
# 4. 启动 Nginx
# 注意:直接运行 `nginx` 命令,容器会在前台运行。
# 使用 `-g 'daemon off;'` 参数让 Nginx 在前台运行,这是 Docker 容器的最佳实践。
CMD ["nginx", "-g", "daemon off;"]
关键点说明:
- 镜像继承 :
FROM my-centos:7-with-repo使得新镜像包含了之前所有的修改(换源、安装工具)。 - 服务安装 :
yum install -y nginx直接从配置好的阿里云源安装 Nginx,无需额外配置。 - 前台运行 :Docker 容器设计为在前台运行一个主进程。如果 Nginx 以守护进程(daemon)模式启动,它会立即启动然后退出,导致容器停止。
-g 'daemon off;'这个参数强制 Nginx 保持在前台运行。
3.2 构建 Nginx 服务镜像
在 nginx-service 目录下执行构建命令:
docker build -t my-nginx:custom .
3.3 运行并测试 Nginx 容器
构建完成后,运行这个新的镜像。
# 运行容器,并将容器的 80 端口映射到宿主机的 8080 端口
docker run -d --name my-nginx-app -p 8080:80 my-nginx:custom
-
-d: 以后台(detached)模式运行容器。 -
--name my-nginx-app: 为容器指定一个名称,便于管理。 -
-p 8080:80: 端口映射,格式为宿主机端口:容器端口。将宿主机的 8080 端口映射到容器的 80 端口。
现在,你可以在宿主机上打开浏览器,访问 http://localhost:8080 ,或者使用 curl 命令测试:
curl http://localhost:8080
你应该能看到我们自定义的 HTML 内容:“Hello from Custom Docker Image with Nginx!”。
3.4 管理容器
查看容器运行状态和日志:
# 查看正在运行的容器
docker ps
# 查看指定容器的日志
docker logs my-nginx-app
# 进入正在运行的容器(如果需要调试)
# docker exec -it my-nginx-app /bin/bash
停止和删除容器:
# 停止容器
docker stop my-nginx-app
# 删除已停止的容器
docker rm my-nginx-app
# 如果想强制删除正在运行的容器,可以加 -f 参数
# docker rm -f my-nginx-app
4. 进阶:使用多阶段构建优化镜像
上面的例子分成了两个镜像构建步骤,清晰但产生了中间镜像。Docker 支持 多阶段构建 ,可以在一个 Dockerfile 中完成多个构建阶段,并且只将最终需要的文件复制到最终镜像,从而显著减小镜像体积。
下面是一个结合了换源和安装 Nginx 的多阶段构建示例(实际上本例单阶段即可,此处用于演示概念):
# 第一阶段:构建阶段,用于准备环境或编译(本例中用于换源和安装)
FROM centos:7 AS builder
# 换源操作
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 clean all && yum makecache
# 安装 Nginx 并创建首页
RUN yum install -y nginx && \
echo "<h1>Multi-stage Build Nginx</h1>" > /usr/share/nginx/html/index.html
# 第二阶段:最终镜像
FROM centos:7
# 从 builder 阶段复制已安装的 Nginx RPM 数据库和文件(简化示例,实际更复杂)
# 注意:直接复制二进制文件可能缺少依赖。更常见的多阶段用于编译型语言。
# 对于 yum 安装,更简单的方式是直接在最终阶段运行安装命令。
# 这里为了演示,我们换一种思路:在最终阶段换源并安装。
# 复制 builder 阶段换源后的仓库配置文件
COPY --from=builder /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/
# 在最终阶段基于换好的源安装 Nginx
RUN yum install -y nginx && \
yum clean all
# 复制 builder 阶段生成的首页
COPY --from=builder /usr/share/nginx/html/index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
说明 :对于像 Nginx 这样直接通过包管理器安装的场景,多阶段构建的优势不大,因为安装过程本身就会在最终镜像中产生层。多阶段构建的真正威力在于 编译环境 和 运行环境 的分离。例如,用一个包含 GCC 的大镜像编译 Go 程序,然后将编译好的、不依赖编译环境的二进制文件复制到一个很小的 alpine 基础镜像中运行。
对于我们的需求,一个更简洁、高效的单阶段 Dockerfile 如下:
FROM centos:7
# 一次性完成所有系统级操作,减少镜像层数
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 clean all && yum makecache && \
yum install -y nginx vim wget && \
yum clean all && \
echo "<h1>Optimized Nginx Image</h1>" > /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
最佳实践 :将相关的 RUN 指令用 && 连接起来,并在最后清理缓存( yum clean all ),这样可以减少镜像的层数,并且清理掉安装包缓存,让镜像体积更小。
5. 常见问题与排查思路
在定制镜像和运行容器的过程中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
docker build 失败,提示 Failed to download metadata for repo | 1. 网络问题,无法访问配置的镜像源。 2. 镜像源配置文件错误或链接失效。 3. 基础镜像版本与仓库配置不匹配(如 CentOS 8 用了 CentOS 7 的 repo)。 | 1. 在容器内尝试 curl -I 测试网络连通性。 2. 检查 Dockerfile 中 curl 下载的 repo 文件 URL 是否正确。 3. 确保 FROM 指定的基础镜像版本与使用的 .repo 文件版本一致。 |
| 容器启动后立即退出 | 1. 容器内没有前台进程在运行。 2. CMD 或 ENTRYPOINT 指定的命令执行失败。 | 1. 使用 docker logs <容器名> 查看启动日志。 2. 确保服务启动命令是前台运行模式(如 Nginx 用 daemon off; )。 3. 可以先用 CMD ["/bin/bash"] 或 CMD ["sleep", "infinity"] 启动容器,然后 exec 进入手动测试命令。 |
| 宿主机无法访问容器的服务 | 1. 容器端口没有映射到宿主机(缺少 -p 参数)。 2. 宿主机防火墙阻止了端口访问。 3. 容器内服务没有监听在 0.0.0.0 上。 | 1. 检查 docker run 命令是否包含 -p <宿主机端口>:<容器端口> 。 2. 检查宿主机防火墙规则(如 firewall-cmd 或 ufw )。 3. 进入容器检查服务进程和监听地址 netstat -tlnp 。 |
| 镜像体积过大 | 1. 每一层 RUN 指令都产生了缓存或临时文件。 2. 安装了不必要的软件包。 | 1. 在 RUN 指令最后合并清理命令,如 yum clean all && rm -rf /var/cache/yum 。 2. 使用多阶段构建,只复制必要的文件到最终镜像。 3. 考虑使用更小的基础镜像,如 alpine 。 |
COPY 文件到容器内失败 | 构建上下文中不存在要复制的文件。 | 确保要 COPY 的文件或目录位于 docker build 命令指定的上下文路径( . )下。 |
6. 最佳实践与工程建议
遵循以下实践,能让你的 Docker 镜像更安全、高效和可维护。
-
使用特定版本的基础镜像 : 避免使用
latest标签,因为它会变化,导致构建行为不可预测。始终指定稳定版本,如centos:7.9.2009、nginx:1.24-alpine。FROM centos:7.9.2009 -
合并 RUN 指令,清理缓存 : 尽可能将多个
RUN指令合并为一个,用&&连接,并在最后清理包管理器缓存,这能减少镜像层数和最终体积。RUN yum update -y && \ yum install -y package1 package2 && \ yum clean all && \ rm -rf /var/cache/yum -
使用
.dockerignore文件 : 在构建上下文目录创建.dockerignore文件,排除不需要的文件(如日志、git目录、临时文件),可以加速构建过程并避免将敏感文件意外打入镜像。**/.git **/.log **/tmp Dockerfile* docker-compose* README.md -
以非 root 用户运行容器 (安全): 默认情况下,容器内的进程以 root 用户运行。为了降低安全风险,应在 Dockerfile 中创建并使用非 root 用户来运行应用。
RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser CMD ["nginx", "-g", "daemon off;"]注意:如果以非 root 用户运行,需要确保该用户对必要的目录(如日志目录、临时目录)有写权限。
-
合理使用
COPY和ADD:COPY用于复制本地文件到镜像。ADD功能类似,但增加了自动解压和从 URL 下载的功能(不推荐从 URL 下载,因为构建缓存无效)。在大多数情况下,优先使用COPY。 -
为镜像打上明确的标签 : 使用有意义的标签,如版本号、构建日期或 Git 提交哈希。
docker build -t myapp:1.0.0 -t myapp:latest . -
扫描镜像安全漏洞 : 使用
docker scan命令(或集成到 CI/CD 中)对构建的镜像进行安全漏洞扫描。docker scan my-nginx:custom -
生产环境考虑 :
- 日志 :确保应用日志输出到
stdout和stderr,这样可以通过docker logs查看,便于集中日志收集。 - 健康检查 :在 Dockerfile 中使用
HEALTHCHECK指令,或在docker run时使用--health-cmd参数,让 Docker 引擎能够监控容器内服务的健康状态。 - 资源限制 :使用
-m、--cpus等参数为容器设置内存和 CPU 限制,防止单个容器耗尽主机资源。
- 日志 :确保应用日志输出到
通过本教程,你不仅学会了如何定制一个带有自定义 Yum 仓库的 Docker 镜像,还掌握了在容器内安装部署服务(Nginx)的完整流程,并了解了多阶段构建、常见问题排查以及一系列生产级的最佳实践。这套方法可以推广到任何基于 Yum/DNF 的 Linux 发行版(如 RHEL、Rocky Linux、AlmaLinux)和任何需要通过包管理器安装的服务(如 MySQL、Redis、Python 等)。下次当你需要为一个特定环境准备 Docker 镜像时,就可以从容地从编写 Dockerfile 开始了。
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度

422

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



