Docker镜像定制:从配置Yum仓库到部署Nginx服务的完整实践

🚀 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 环境与工具准备

为了完成本教程,你需要准备以下环境:

  1. 操作系统 :任何支持 Docker 的 Linux 发行版(如 Ubuntu 20.04/22.04, CentOS 7/8)、macOS 或 Windows 10/11(需启用 WSL2)。本文命令以 Linux/macOS 的 Bash 环境为例。
  2. Docker 引擎 :确保已安装并运行 Docker。你可以通过以下命令验证:
    docker --version
    docker info
    
    如果未安装,请参考 Docker 官方文档或使用系统包管理器安装。对于生产环境,不建议使用一键安装脚本,应遵循官方的最佳实践进行安装。
  3. 文本编辑器 :用于编写 Dockerfile 和配置文件,如 Vim、VS Code、Nano 等。
  4. 网络连接 :需要能够访问互联网以下载基础镜像和软件包。

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 镜像更安全、高效和可维护。

  1. 使用特定版本的基础镜像 : 避免使用 latest 标签,因为它会变化,导致构建行为不可预测。始终指定稳定版本,如 centos:7.9.2009 nginx:1.24-alpine

    FROM centos:7.9.2009
    
  2. 合并 RUN 指令,清理缓存 : 尽可能将多个 RUN 指令合并为一个,用 && 连接,并在最后清理包管理器缓存,这能减少镜像层数和最终体积。

    RUN yum update -y && \
        yum install -y package1 package2 && \
        yum clean all && \
        rm -rf /var/cache/yum
    
  3. 使用 .dockerignore 文件 : 在构建上下文目录创建 .dockerignore 文件,排除不需要的文件(如日志、git目录、临时文件),可以加速构建过程并避免将敏感文件意外打入镜像。

    **/.git
    **/.log
    **/tmp
    Dockerfile*
    docker-compose*
    README.md
    
  4. 以非 root 用户运行容器 (安全): 默认情况下,容器内的进程以 root 用户运行。为了降低安全风险,应在 Dockerfile 中创建并使用非 root 用户来运行应用。

    RUN groupadd -r appuser && useradd -r -g appuser appuser
    USER appuser
    CMD ["nginx", "-g", "daemon off;"]
    

    注意:如果以非 root 用户运行,需要确保该用户对必要的目录(如日志目录、临时目录)有写权限。

  5. 合理使用 COPY ADD COPY 用于复制本地文件到镜像。 ADD 功能类似,但增加了自动解压和从 URL 下载的功能(不推荐从 URL 下载,因为构建缓存无效)。在大多数情况下,优先使用 COPY

  6. 为镜像打上明确的标签 : 使用有意义的标签,如版本号、构建日期或 Git 提交哈希。

    docker build -t myapp:1.0.0 -t myapp:latest .
    
  7. 扫描镜像安全漏洞 : 使用 docker scan 命令(或集成到 CI/CD 中)对构建的镜像进行安全漏洞扫描。

    docker scan my-nginx:custom
    
  8. 生产环境考虑

    • 日志 :确保应用日志输出到 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 折。 👉 点击领海量免费额度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值