这类 Docker 基础操作,新手最容易卡住的地方往往不是命令本身,而是“为什么我的容器里装不了软件”、“为什么我做的镜像那么大”、“为什么容器重启后配置就丢了”。今天我们就围绕“定制简单镜像”和“容器内安装部署服务”这两个核心动作,拆解背后的逻辑和实操细节。如果你刚接触 Docker,想搞明白如何把一个干净的 CentOS 容器,一步步变成能跑 Nginx 或 Zabbix 的服务环境,并且理解每一步的用意,那这篇经验梳理应该能帮你避开不少弯路。

很多人一上来就搜 docker run centos ,然后 yum install ,发现报错“无法找到仓库”,或者装完服务后,不知道如何保存状态、如何配置。这其实是因为没理清 Docker 的“层”概念和容器网络访问机制。我会从最基础的“启动一个能联网的容器”开始,带你走通“配置Yum源 -> 安装软件 -> 保存为镜像 -> 部署服务”的完整闭环。整个过程,我会重点解释哪些操作是临时的,哪些是持久的,以及如何用最少的命令和最小的镜像体积来完成目标。

1. 先理清目标:我们要定制一个什么样的“服务镜像”?

在动手敲命令之前,得先想清楚最终要什么。是仅仅在容器里体验一下 yum 安装过程,还是要做出一个可以分发给别人、一键启动就能提供服务的镜像?这决定了我们的操作路径。

目标A:临时学习/测试 你只需要一个能联网、能 yum 安装软件的容器环境,测试完就丢弃。这种情况,重点在于快速配置好容器内的网络和仓库,不关心镜像的复用性。

目标B:制作可复用的服务镜像 你需要制作一个像 nginx:latest 那样的镜像,里面预装好了 Nginx、Zabbix Agent 或其他服务,并且配置了正确的启动命令。这样,任何人拿到这个镜像, docker run 之后服务就能跑起来。这是我们这次的重点。

对于目标B,一个高效的思路是: 先在一个临时容器里完成所有调试和安装,确认无误后,再将其固化为一个新镜像 。而不是试图在 Dockerfile 里一遍遍构建、试错(虽然 Dockerfile 是最终推荐方式,但调试阶段直接操作容器更直观)。

2. 第一步:启动一个“干净”且“能联网”的 CentOS 容器

几乎所有问题的起点都是这里。如果你直接用 docker run -it centos:7 bash 启动容器,大概率会发现 yum update ping baidu.com 失败。这不是镜像坏了,而是容器默认的网络配置和仓库配置可能不适用于你的环境。

2.1 选择合适的基础镜像

首先,确保你拉取的是官方或可信的 CentOS 镜像。不建议使用 latest 标签,明确指定版本更稳妥。

# 拉取 CentOS 7 镜像
docker pull centos:7

为什么是 centos:7 而不是 centos:latest ?因为 latest 标签可能指向 CentOS Stream 或其他版本,其软件仓库和系统路径可能与 CentOS 7 不同,会导致后续安装命令不一致。固定版本能保证环境一致性。

2.2 以“可安装软件”的模式启动容器

单纯 -it 交互运行是不够的。为了让容器内部能访问外部网络以下载软件包,通常不需要特殊参数,因为 Docker 默认会为容器分配网络。但如果你的宿主机处于特殊网络环境(如某些企业内网需要代理),则需要在启动容器时传递环境变量,或在 Docker 服务端配置代理。

一个更实用的启动命令是:

# 启动一个名为 my_centos 的临时容器,并进入bash
docker run -it --name my_centos --rm centos:7 bash

参数解释:

  • -it :分配一个伪终端并保持标准输入打开,让我们可以交互操作。
  • --name my_centos :给容器起个名字,方便后续操作(如 docker exec docker commit )。
  • --rm :容器退出时自动删除它。因为我们只是用它来调试安装过程,最终会提交成新镜像,这个临时容器不需要保留。
  • centos:7 bash :基于 centos:7 镜像启动容器,并执行 bash 命令。

进入容器后,你应该先测试网络连通性:

# 在容器内执行
ping -c 2 114.114.114.114

如果 ping 不通,大概率是宿主机的防火墙或 Docker 网络驱动问题。一个快速的排查方法是检查容器的网络模式:

# 在宿主机上,查看容器的网络信息
docker inspect my_centos | grep -A 10 "NetworkSettings"

通常,只要宿主机能上网,使用默认的 bridge 网络模式的容器就能通外网。如果不行,可以尝试在启动时指定 --network host (使用宿主机网络栈),但这会带来安全性和端口冲突的考虑,仅用于测试。

3. 第二步:在容器内配置可用的 Yum 仓库

这是核心环节。CentOS 官方仓库地址可能因为网络延迟或镜像同步问题访问缓慢甚至失败。我们需要将仓库源替换为国内镜像,如阿里云、清华大学的镜像源。

3.1 备份原有仓库配置

进入容器后,首先备份默认的 repo 文件,这是一个好习惯。

# 进入容器后执行
cd /etc/yum.repos.d/
mkdir bak
mv *.repo bak/

3.2 下载并配置国内镜像源

以阿里云镜像源为例(适用于 CentOS 7):

# 下载阿里云的CentOS-Base.repo
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

# 如果容器内没有curl,可以先安装curl(用官方源临时安装,速度可能慢)
# yum install -y curl wget
# 或者,更直接的方法:在宿主机下载好repo文件,然后复制到容器内。

为什么强调这个步骤? 因为很多 Docker 教程忽略了容器内 yum 源的配置,直接假设 yum install 能工作。在实际生产环境或国内网络下,不换源会导致安装速度极慢甚至超时失败,让你误以为是 Docker 或镜像的问题。

3.3 清理并生成缓存

配置新源后,执行以下命令使其生效:

# 清理旧缓存
yum clean all
# 生成新缓存
yum makecache
# 可以更新系统(非必须,但能测试仓库是否真正可用)
yum update -y

如果 yum makecache 能成功执行,没有报错,说明 Yum 仓库已经配置正确。此时,你的容器已经具备了安装任何 CentOS 软件包的能力。

4. 第三步:在容器内安装并配置目标服务

仓库搞定后,安装软件就很简单了。我们以安装 Nginx 为例,演示一个完整的服务部署流程。

4.1 安装 Nginx

yum install -y nginx

安装完成后,不要急着启动。我们先看看 Nginx 被安装到了哪里,配置文件在哪。

# 查看nginx安装位置
which nginx
# 通常为 /usr/sbin/nginx

# 查看配置文件目录
ls -la /etc/nginx/

4.2 编写一个简单的自定义配置(可选)

为了体现“定制”,我们可以修改默认的欢迎页面或端口。例如,修改默认的 index.html

# 备份原首页
cp /usr/share/nginx/html/index.html /usr/share/nginx/html/index.html.bak

# 创建一个新的首页
echo "<h1>Hello from My Custom Docker Nginx Container!</h1>" > /usr/share/nginx/html/index.html

4.3 测试服务能否在容器内正常运行

在将容器提交为镜像之前,务必在容器内部先测试服务是否能启动。

# 启动nginx
nginx
# 或者使用 systemctl?注意:容器内通常没有 systemd,直接运行二进制文件。
# 检查nginx进程
ps aux | grep nginx
# 测试本地访问(容器内)
curl http://localhost

如果 curl 能返回我们刚才写的 “Hello from My Custom Docker Nginx Container!”,说明 Nginx 安装和配置成功。

关键点 :在容器里,服务通常以前台模式运行,或者通过脚本控制。我们最终制作镜像时,需要指定一个启动命令( CMD ENTRYPOINT )。现在在调试容器里,我们可以先手动启动验证。

4.4 考虑服务的持久化配置

如果服务有需要持久化的数据或配置(如 Nginx 的日志文件、Web 根目录),我们需要规划好。在 Docker 中,持久化数据通常通过“卷(Volume)”或“绑定挂载(Bind Mount)”来实现,而不是保存在容器可写层。

对于镜像制作阶段,我们只需要确保服务 默认 的配置和启动方式是合理的。数据持久化是容器运行时通过 -v 参数来处理的。

5. 第四步:将配置好的容器提交为定制镜像

经过上面几步,我们已经在 my_centos 容器里安装好了 Nginx 并做了简单定制。现在,我们需要把这个容器的当前状态“冻结”下来,保存为一个新的镜像。

5.1 退出容器但不删除

注意,我们启动容器时用了 --rm 参数,一旦退出容器就会自动删除。为了提交镜像,我们需要一个 已停止但未删除 的容器。

因此,更稳妥的流程是: 先不用 --rm 启动容器,调试完成后,退出容器,再提交

实际操作流程修正如下:

  1. 启动容器(不用--rm)
    docker run -it --name my_centos_builder centos:7 bash
    
  2. 在容器内完成上述所有步骤(配置yum源、安装nginx、修改首页)。
  3. 退出容器 :在容器内执行 exit 。此时容器会停止,但不会被删除。
  4. 提交容器为镜像
    docker commit my_centos_builder my-nginx:v1
    
    命令解释: docker commit [容器名] [新镜像名:标签] 这会将容器 my_centos_builder 的可写层(即我们所有的修改)与原来的 centos:7 只读层合并,创建一个新的镜像 my-nginx:v1

5.2 验证新镜像

提交后,检查镜像列表,并基于新镜像运行一个容器来验证服务。

# 查看镜像
docker images | grep my-nginx

# 基于新镜像运行一个容器,并让nginx在前台启动
docker run -d --name nginx-test -p 8080:80 my-nginx:v1 nginx -g "daemon off;"

参数解释:

  • -d :后台运行。
  • -p 8080:80 :将宿主机的8080端口映射到容器的80端口。
  • my-nginx:v1 :我们刚定制的镜像。
  • nginx -g "daemon off;" :覆盖镜像的默认CMD,以前台模式启动Nginx(这是让容器保持运行的最佳实践)。

现在,在宿主机上打开浏览器访问 http://localhost:8080 ,应该能看到我们定制的欢迎页面。

5.3 优化:为镜像定义默认启动命令

上面我们是在 docker run 时指定了启动命令。更好的做法是把启动命令做到镜像里。这需要用到 Dockerfile,但我们可以通过 docker commit 时指定 --change 参数来模拟。

不过,更规范的做法是回到起点,用 Dockerfile 来构建镜像。因为 docker commit 虽然直观,但不利于自动化、版本管理和清晰了解镜像的构建过程。它会把操作历史“黑盒化”。

6. 第五步:使用 Dockerfile 重构构建流程(最佳实践)

虽然我们通过交互式操作完成了定制,但生产环境应该使用 Dockerfile。Dockerfile 是一个文本文件,包含了一系列构建指令,能实现可重复、透明化的构建。

6.1 创建 Dockerfile

在宿主机上创建一个工作目录,并新建 Dockerfile 文件:

# 使用 CentOS 7 作为基础镜像
FROM centos:7

# 维护者信息(可选)
LABEL maintainer="your-email@example.com"

# 配置国内Yum源
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo \
    && yum clean all \
    && yum makecache

# 安装 Nginx
RUN yum install -y nginx

# 定制欢迎页面
RUN echo "<h1>Hello from My Custom Docker Nginx Container (Built by Dockerfile)!</h1>" > /usr/share/nginx/html/index.html

# 暴露80端口
EXPOSE 80

# 设置容器启动时执行的命令
CMD ["nginx", "-g", "daemon off;"]

6.2 构建镜像

在 Dockerfile 所在目录执行:

docker build -t my-nginx-dockerfile:v1 .

-t 指定镜像标签, . 表示使用当前目录下的 Dockerfile。

6.3 运行并测试

docker run -d --name nginx-df -p 8081:80 my-nginx-dockerfile:v1

访问 http://localhost:8081 验证。

使用 Dockerfile 的优势

  1. 可重复 :任何人在任何地方,只要有这个文件,都能构建出一模一样的镜像。
  2. 透明 :每一步操作都记录在案,易于理解和维护。
  3. 层缓存 :Docker 会缓存每一层,如果某一步没有修改,下次构建会直接使用缓存,极大加快构建速度。
  4. 易于版本管理 :Dockerfile 可以放入 Git 仓库,与代码一同管理。

7. 常见问题与排查思路

即使按照步骤操作,你也可能会遇到一些问题。这里列出几个典型问题及其排查路径。

7.1 容器内无法联网(ping/curl/yum 失败)

  1. 检查容器网络模式 docker inspect <容器名> | grep -i networkmode 。如果是 none ,则容器无网络。重新以 bridge host 模式启动。
  2. 检查宿主机防火墙 :某些系统(如 CentOS 7 firewalld, Ubuntu ufw)可能阻止了 Docker 的网络流量。可以尝试暂时关闭防火墙测试(仅用于排查,生产环境需谨慎)。
  3. 检查 Docker 服务状态 systemctl status docker ,确保 Docker 服务正常运行。
  4. 检查 DNS 配置 :容器内 /etc/resolv.conf 文件是否正常。可以尝试在 docker run 时添加 --dns 8.8.8.8 指定 DNS。

7.2 Yum 安装软件速度慢或失败

  1. 确认仓库源已正确替换 cat /etc/yum.repos.d/CentOS-Base.repo ,检查里面的 baseurl mirrorlist 是否是国内镜像地址。
  2. 执行 yum clean all && yum makecache :清理旧缓存并建立新缓存。
  3. 检查网络连通性 curl -I mirrors.aliyun.com ,看是否能连接到镜像站。
  4. 容器时间不正确 date 命令查看,如果时间偏差太大,可能导致 SSL 证书验证失败。可以在 Dockerfile 或启动时同步时间: RUN yum install -y ntpdate && ntpdate time.windows.com

7.3 制作的镜像体积过大

  1. 清理 Yum 缓存 :在 Dockerfile 的每个 RUN yum install 命令后,最好清理缓存。
    RUN yum install -y nginx && yum clean all
    
  2. 合并 RUN 指令 :多个 RUN 指令会创建多个镜像层。尽量合并相关操作为一个 RUN 指令,用 && 连接。
  3. 使用更小的基础镜像 :考虑使用 centos:7 的瘦身版,如 centos:7-slim ,或者 Alpine Linux(但软件包管理不同)。

7.4 容器启动后服务立即退出

  1. 检查前台/后台进程 :容器需要至少一个前台进程才能保持运行。如果镜像的 CMD 是启动一个后台服务(如 systemctl start nginx ),那么命令执行完进程就结束了,容器也会退出。 必须以前台模式运行主进程 ,如 nginx -g "daemon off;"
  2. 查看容器日志 docker logs <容器名> ,这里通常会有服务启动失败的错误信息。
  3. 检查端口冲突 docker run -p 80:80 ,如果宿主机80端口已被占用,容器可能启动失败。换一个宿主机端口,如 -p 8080:80

8. 扩展:部署更复杂的服务(如 Zabbix Agent)

理解了 Nginx 的部署流程,部署其他服务(如 Zabbix Agent、自定义应用)的思路是一样的。

  1. 准备基础环境 :同样从配置 Yum 源开始。
  2. 查找安装方法 :对于 Zabbix Agent,可能需要先添加 Zabbix 官方的 Yum 仓库。
    # 在Dockerfile中添加
    RUN rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/7/x86_64/zabbix-release-6.0-4.el7.noarch.rpm
    RUN yum install -y zabbix-agent
    
  3. 配置服务 :将 Agent 的配置文件(如 /etc/zabbix/zabbix_agentd.conf )中 Server ServerActive 指向你的 Zabbix Server IP。 注意 :这个 IP 通常不能写死为 127.0.0.1 ,因为容器内是独立的网络命名空间。最好在容器启动时通过环境变量传入。
  4. 定义启动命令 CMD ["zabbix_agentd", "-f", "-c", "/etc/zabbix/zabbix_agentd.conf"]

核心经验 :对于需要连接外部其他服务的容器(如 Agent 要连 Server),配置文件的 IP、主机名等 不要固化在镜像里 。应该通过环境变量( -e )或在容器启动时挂载外部配置文件( -v )的方式动态注入。这样同一个镜像才能在不同的环境中复用。

整个过程走下来,你会发现定制 Docker 镜像的关键路径非常清晰: 启动基础容器 -> 配置环境(网络、仓库)-> 安装配置软件 -> 验证 -> 固化(commit或Dockerfile) 。其中,Yum仓库配置是第一步的基石,而理解容器内服务的前台运行模式,则是镜像能否成功运行的最后一道关卡。下次当你需要把一个传统服务塞进容器时,不妨先按这个顺序在临时容器里走一遍流程,调试成功后再写成 Dockerfile,效率会高很多。

更多推荐