Docker实战:从定制镜像到容器内软件配置的完整指南
你是不是也遇到过这样的场景:在本地开发环境跑得好好的服务,一到测试或生产环境就各种依赖缺失、版本冲突?或者团队新成员入职,光是配环境就要花掉一整天?
这正是 Docker 要解决的核心痛点。它通过“集装箱”式的封装,将应用及其所有依赖打包成一个标准化的镜像,实现了“一次构建,处处运行”。但很多初学者在接触 Docker 时,往往止步于 docker run 一个现成的镜像,对于如何从零开始定制自己的镜像、如何在容器内部进行软件安装和配置,仍然感到无从下手。
这篇文章要解决的,正是这个从“会用”到“会做”的关键跨越。我们将聚焦两个核心实战技能: 如何基于一个基础镜像,定制一个包含自己服务的简单镜像 ,以及 如何在运行的容器内部,动态配置软件源(如 Yum 仓库)并安装部署服务 。这不仅是理解 Docker 分层构建和容器运行机制的关键,更是将 Docker 真正用于日常开发和部署的必备能力。
读完本文,你将能清晰地掌握 Dockerfile 编写、镜像构建、容器内软件管理的完整流程,并能够独立完成一个简单服务的容器化封装与部署。
1. 为什么需要定制镜像与容器内配置?
在深入操作之前,我们先明确这两个场景的价值,这决定了你是否需要深入学习。
场景一:定制简单镜像 当你需要部署一个自己编写的 Python 脚本、一个 Go 语言编译的二进制文件,或者一个需要特定系统依赖的 Java 应用时,直接使用官方的 python:alpine 或 openjdk:11 镜像往往不够。你需要将自己的代码、配置文件“塞”进去,并可能安装额外的系统包。这个过程,就是通过编写 Dockerfile 来定制镜像。它的核心价值在于 固化环境 和 实现自动化部署 。镜像一旦构建成功,在任何装有 Docker 的环境中,都能以完全一致的方式启动你的服务。
场景二:为容器配置 Yum 仓库并安装服务 这个场景更偏向于 动态调试 和 快速验证 。想象一下,你拉取了一个非常干净的 centos:7 基础镜像并启动了一个容器,发现里面缺少 vim 、 net-tools 甚至 wget 等调试工具。或者,你想在容器内临时安装一个服务(如 nginx )进行功能测试,但基础镜像的默认软件源速度极慢甚至不可用。这时,你就需要在运行的容器内部,手动配置一个高速的 Yum 仓库(如阿里云、清华源),然后安装所需软件。虽然最佳实践是 将这类依赖的安装固化到 Dockerfile 中 ,但掌握容器内的动态配置能力,对于问题排查、临时测试和学习理解容器内部机制至关重要。
简单来说: 定制镜像是为了交付最终产品,而容器内配置是为了过程中的调试与探索。 两者相辅相成,都是 Docker 能力矩阵中不可或缺的部分。
2. 核心概念快速理解
在动手之前,快速厘清几个容易混淆的概念:
- 镜像 (Image) :一个只读的模板,包含了运行应用所需的文件系统、依赖和配置。你可以把它理解为一个“应用程序的安装包”或“系统的快照”。镜像是分层的,每一层都代表一次修改(如添加一个文件、运行一条命令)。
- 容器 (Container) :镜像的一个运行实例。当你运行一个镜像时,Docker 会创建一个可写的容器层(称为“容器层”),叠加在只读的镜像层之上。所有对运行中系统的修改(如创建文件、安装软件)都发生在这个容器层中。 容器停止后,这个可写层默认会被丢弃。
- Dockerfile :一个文本文件,包含了一系列用于构建镜像的指令(如
FROM,RUN,COPY,CMD等)。docker build命令通过执行这些指令,自动生成镜像。 - 仓库 (Repository) :这里有两个“仓库”概念需要区分:
- Docker 镜像仓库 (Registry) :如 Docker Hub、阿里云容器镜像服务,用于存储和分发 Docker 镜像。
- Linux 软件包仓库 (如 Yum Repository) :用于存储 RPM 软件包及其元数据,
yum或dnf命令从这里下载并安装软件。本文中“配置 Yum 仓库”指的是后者。
关键关系 : Dockerfile -> ( docker build ) -> 镜像 -> ( docker run ) -> 容器 。定制镜像修改的是左边的链条(持久化),而在容器内配置 Yum 仓库影响的是右边的单个容器实例(临时性)。
3. 环境准备与前置检查
为了完成后续所有操作,你需要一个可用的 Docker 环境。本文假设你使用的是 CentOS 7 或 Rocky Linux/AlmaLinux 等 RHEL 系发行版,这也是配置 Yum 仓库最典型的场景。
3.1 系统与内核要求
Docker 对系统内核版本有要求。打开终端,执行以下命令检查:
uname -r
确保输出内核版本 高于 3.10 。例如 3.10.0-1160.el7.x86_64 是符合要求的。
3.2 安装 Docker CE
如果你的系统尚未安装 Docker,可以参考以下最简步骤。这里采用国内常用的阿里云镜像源来加速安装。
-
卸载旧版本(如有) :
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine -
安装必要的依赖包 :
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 -
配置阿里云 Docker CE Yum 仓库 :
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo这个命令会在
/etc/yum.repos.d/目录下添加一个docker-ce.repo文件,将软件源指向阿里云镜像。 -
更新 Yum 缓存并安装 Docker CE :
sudo yum makecache fast sudo yum install -y docker-ce docker-ce-cli containerd.io -
启动 Docker 服务并设置开机自启 :
sudo systemctl start docker sudo systemctl enable docker -
验证安装 :
sudo docker run hello-world如果能看到 “Hello from Docker!” 等欢迎信息,说明 Docker 已安装并运行正常。
-
(可选)配置用户组 :为了避免每次使用
docker命令都加sudo,可以将当前用户加入docker组。sudo usermod -aG docker $USER重要 :执行此命令后,你需要 完全退出当前终端并重新登录 ,或者新开一个终端,用户组变更才会生效。
至此,你的 Docker 环境已经准备就绪。
4. 实战一:定制一个简单的自定义镜像
我们的目标是创建一个简单的 Web 服务镜像。它基于 nginx 官方镜像,但将我们自定义的 HTML 页面替换掉默认的欢迎页。
4.1 创建项目目录与文件
首先,创建一个清晰的工作目录。
mkdir -p ~/docker-demo/custom-nginx
cd ~/docker-demo/custom-nginx
在该目录下,创建两个文件:
Dockerfile:构建镜像的蓝图。index.html:我们自定义的首页。
4.2 编写自定义 HTML 页面
编辑 index.html 文件,内容如下:
<!DOCTYPE html>
<html>
<head>
<title>My Custom Nginx in Docker</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding-top: 50px;
background-color: #f0f0f0;
}
h1 {
color: #333;
}
p {
color: #666;
}
</style>
</head>
<body>
<h1>🚀 Hello from My Custom Docker Image!</h1>
<p>This page is served from a custom Nginx image built by me.</p>
<p>Image build time: <span id="build-time"></span></p>
<script>
document.getElementById('build-time').textContent = new Date().toLocaleString();
</script>
</body>
</html>
4.3 编写 Dockerfile
这是构建镜像的核心。在同一个目录下创建 Dockerfile (注意没有后缀名):
# 1. 指定基础镜像。我们使用官方的 nginx 镜像作为起点。
FROM nginx:alpine
# 2. 维护者信息(已弃用,但可作为元数据保留)
LABEL maintainer="your-email@example.com"
# 3. 将宿主机当前目录下的 index.html 文件,
# 复制到容器内 nginx 的默认网站根目录。
# alpine 版 nginx 的默认根目录是 /usr/share/nginx/html
COPY index.html /usr/share/nginx/html/
# 4. (可选)暴露端口。nginx 镜像默认已暴露 80 端口,此处显式声明以示清晰。
EXPOSE 80
# 5. 容器启动时执行的命令。
# nginx 镜像的默认 CMD 已经是启动 nginx,这里可以省略。
# 如果基础镜像没有,可以写:CMD ["nginx", "-g", "daemon off;"]
这个 Dockerfile 做了四件事:
- FROM :定义了构建的起点。
alpine版本非常小巧。 - LABEL :为镜像添加元数据。
- COPY :将本地文件复制到镜像内的指定路径。这是“定制化”的关键步骤。
- EXPOSE :声明容器运行时监听的端口,是一个元数据,方便使用者知道。
4.4 构建自定义镜像
在 Dockerfile 所在的目录( ~/docker-demo/custom-nginx )执行构建命令:
docker build -t my-custom-nginx:1.0 .
命令解析:
docker build:构建镜像的命令。-t my-custom-nginx:1.0:为构建成功的镜像打上标签(Tag)。格式为名称:版本。my-custom-nginx是镜像名,1.0是标签。.: 这个点非常重要 ,它指定了构建上下文(Build Context)的路径。Docker 客户端会将这个路径下的所有文件(除了.dockerignore声明的)发送给 Docker 守护进程。Dockerfile和index.html都需要在这个上下文中。
构建过程中,你会看到 Docker 逐层执行 Dockerfile 中的指令并输出日志。
4.5 运行并验证自定义镜像
构建成功后,运行这个自定义的容器:
docker run -d -p 8080:80 --name my-nginx-container my-custom-nginx:1.0
命令解析:
-d:后台运行(detached mode)。-p 8080:80:端口映射。将宿主机的8080端口映射到容器的80端口。--name my-nginx-container:为容器指定一个名字,便于后续管理。my-custom-nginx:1.0:指定要运行的镜像及其标签。
现在,打开你的浏览器,访问 http://localhost:8080 (如果 Docker 运行在远程服务器,请替换 localhost 为服务器 IP)。你应该能看到我们自定义的 HTML 页面,而不是 Nginx 的默认欢迎页。
验证容器运行状态 :
docker ps
你应该能看到名为 my-nginx-container 的容器正在运行。
查看容器日志 :
docker logs my-nginx-container
停止并删除容器 (用于清理):
docker stop my-nginx-container
docker rm my-nginx-container
至此,你已经成功完成了第一个实战:从编写 Dockerfile 开始,构建了一个属于自己的 Docker 镜像,并运行了它。这个镜像 my-custom-nginx:1.0 可以推送到任何 Docker 镜像仓库,供他人或其它环境使用。
5. 实战二:在运行的容器内配置 Yum 仓库并安装服务
现在,我们进入第二个场景:在一个正在运行的、非常干净的基础系统容器内,动态地配置软件源并安装服务。我们以 centos:7 镜像为例。
5.1 启动一个干净的 CentOS 7 容器
首先,拉取镜像(如果本地没有)并启动一个交互式的容器:
docker run -it --name centos-test centos:7 /bin/bash
命令解析:
-it:-i保持标准输入打开,-t分配一个伪终端。两者结合允许你与容器进行交互,就像登录了一台虚拟机。--name centos-test:指定容器名称。centos:7:使用的镜像。/bin/bash:容器启动后执行的命令,这里我们启动一个 Bash shell。
执行成功后,你的终端提示符会变成类似 [root@容器ID /]# 的样子,表示你已经进入了容器内部。
5.2 容器内配置阿里云 Yum 仓库
默认的 CentOS 7 基础镜像的 Yum 源可能速度很慢。我们将其替换为阿里云的镜像源。
-
在容器内的 Shell 中,首先备份原有的 Yum 源配置文件:
cd /etc/yum.repos.d/ mkdir bak mv *.repo bak/ -
下载阿里云为 CentOS 7 提供的 repo 文件:
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo注意 :如果容器内没有
curl命令,可以使用wget(如果也没有,需要先通过其他方式获取文件,或者使用宿主机的文件拷贝进去,见下文备选方案)。 -
清理并重建 Yum 缓存:
yum clean all yum makecache
备选方案:如果容器内没有网络工具 如果容器内既没有 curl 也没有 wget ,你可以从宿主机将 repo 文件复制到容器内。 在宿主机的另一个终端窗口 执行:
# 首先,在宿主机下载阿里云的 repo 文件
curl -o CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
# 然后,使用 docker cp 命令复制到正在运行的容器中
docker cp CentOS-Base.repo centos-test:/etc/yum.repos.d/
# 最后,回到容器内的终端,执行缓存更新
yum clean all
yum makecache
5.3 在容器内安装服务
Yum 源配置好后,就可以安装任何你需要的软件了。例如,安装一个 nginx 服务用于测试,同时安装 vim 和 net-tools 等常用工具。
在容器内的 Shell 中执行:
yum install -y nginx vim net-tools wget
-y 参数表示对所有询问自动回答“是”。
5.4 验证安装并运行服务
- 验证安装 :
nginx -v vim --version ifconfig # 来自 net-tools - 启动 Nginx :
nginx - 检查 Nginx 进程和端口 :
你应该能看到 Nginx 主进程和工作进程,以及 80 端口处于监听状态。ps aux | grep nginx netstat -tlnp | grep :80
重要提醒 :此时 Nginx 服务运行在容器内部,但外部(宿主机)还无法直接访问,因为我们在启动容器时没有做端口映射 ( -p )。
5.5 从容器提交为新镜像(可选但重要)
我们在容器内所做的所有修改(安装软件、配置源)都只存在于这个容器的可写层中。如果容器被删除,这些修改将全部丢失。
如果你想将当前这个配置好环境的状态保存下来,形成一个新的镜像,可以使用 docker commit 命令。 在宿主机的终端 (不是容器内的终端)执行:
# 首先,退出容器(在容器内执行 exit,或按 Ctrl+D)
# 然后,在宿主机执行提交命令
docker commit centos-test my-centos-with-nginx:1.0
命令解析:
centos-test:源容器的名称。my-centos-with-nginx:1.0:新镜像的名称和标签。
提交后,你可以用 docker images 看到这个新镜像。以后就可以直接运行这个镜像,而无需再次执行安装配置步骤:
docker run -it --name new-container my-centos-with-nginx:1.0 /bin/bash
进入后你会发现, nginx 、 vim 等都已经安装好了。
请注意 : docker commit 虽然方便,但在生产环境中并不推荐作为构建镜像的主要方式。因为它是一个“黑盒”操作,无法追溯镜像的生成过程,不利于维护和审计。 标准做法仍然是编写 Dockerfile ,使用 docker build 进行构建。
6. 两种方式的对比与最佳实践
通过以上两个实战,我们体验了 Docker 的两种工作流:
| 特性 | 定制镜像 (Dockerfile) | 容器内配置 (动态修改) |
|---|---|---|
| 目的 | 构建交付物 ,固化应用及其环境。 | 调试、测试、临时修改 运行中的容器。 |
| 可重复性 | 高 。 Dockerfile 是代码,可版本管理,每次构建结果一致。 |
低 。手动操作,难以精确复现。 |
| 自动化 | 完美支持 。可集成到 CI/CD 流水线。 | 不支持 。依赖人工交互。 |
| 镜像层级 | 清晰。每一层对应 Dockerfile 中的一条指令。 |
混乱。 docker commit 会将所有修改打包成单一新层。 |
| 推荐场景 | 所有需要交付或共享的应用环境。 | 仅用于学习、紧急问题排查或一次性实验。 |
最佳实践建议:
- 一切皆代码 :将环境配置和应用打包的过程全部写入
Dockerfile。这是容器化的黄金准则。 - 使用
.dockerignore:在构建上下文目录创建.dockerignore文件,排除不需要发送给 Docker 守护进程的文件(如.git,node_modules, 日志文件),可以加速构建过程并减小镜像体积。 - 选择合适的基础镜像 :优先选择官方镜像,并根据需求选择
-alpine(体积小)、-slim(精简)或特定版本标签。 - 减少镜像层数 :将相关的
RUN命令用&&连接,并用\换行,以减少镜像层数,使镜像更紧凑。# 不推荐 RUN yum update -y RUN yum install -y package1 RUN yum install -y package2 RUN yum clean all # 推荐 RUN yum update -y && \ yum install -y package1 package2 && \ yum clean all - 清理缓存 :在安装软件的同一条
RUN指令中,完成安装后立即清理包管理器缓存(如yum clean all,apt-get clean),可以有效减小最终镜像大小。 - 慎用
docker commit:仅将其作为从临时容器中“拯救”配置的最后手段,而不是常规构建流程。
7. 常见问题与排查思路
在实践过程中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
docker build 失败,提示找不到文件 |
COPY 或 ADD 指令引用的文件不在构建上下文中。 |
检查 docker build 命令最后的 . 指定的路径,以及 Dockerfile 中文件的相对路径。 |
确保所有需要复制的文件都在构建上下文目录或其子目录下。 |
| 容器启动后立即退出 | 容器内没有前台进程在运行。Docker 容器需要至少一个前台进程才能保持运行。 | docker logs <容器名> 查看日志。检查 Dockerfile 中的 CMD 或 ENTRYPOINT 指令。 |
确保启动命令是前台执行(如 nginx -g ‘daemon off;‘ )。对于测试,可以用 docker run -it ... /bin/bash 交互式进入。 |
| 宿主机无法访问容器服务 | 启动容器时没有使用 -p 参数映射端口,或者防火墙/安全组规则阻止。 |
1. docker ps 查看端口映射列。2. 宿主机执行 curl localhost:<映射端口> 。3. 检查宿主机防火墙 ( firewall-cmd ) 或云服务商安全组。 |
确保 docker run 时正确使用 -p <宿主机端口>:<容器端口> 。开放宿主机对应端口的防火墙。 |
容器内无法连接外网(如无法 yum install ) |
容器 DNS 配置问题,或宿主机的网络策略限制。 | 1. 容器内 ping 8.8.8.8 测试网络连通性。2. cat /etc/resolv.conf 查看 DNS。 |
检查宿主机的网络。运行容器时可尝试 --network host (使用主机网络)测试,或指定 DNS --dns 8.8.8.8 。 |
docker run 提示端口已被占用 |
宿主机上该端口已被其他进程(可能是另一个容器)使用。 | netstat -tlnp | grep :<端口号> 查看占用进程。 |
更改 docker run -p 的宿主机端口,或停止占用端口的原有容器。 |
| 镜像体积过大 | 基础镜像过大,或在构建过程中产生了大量中间文件和缓存。 | docker images 查看镜像大小。检查 Dockerfile 中是否有多余的层,是否清理了缓存。 |
使用更小的基础镜像(如 Alpine)。合并 RUN 指令,并在同一层中清理包管理缓存。使用 .dockerignore 。 |
容器内修改文件后, docker commit 的镜像启动失败 |
docker commit 会保存容器当前状态,包括可能不完整的配置或临时文件。 |
对比原基础镜像的启动命令和文件结构。 | 优先使用 Dockerfile 重建 。如果必须 commit,确保容器处于一个稳定、干净的状态(如服务已正常停止)。 |
8. 总结与进阶方向
通过“定制简单镜像”和“容器内配置 Yum 仓库”这两个核心实战,我们走完了 Docker 从静态构建到动态交互的完整闭环。你不仅学会了如何用 Dockerfile 打造一个可交付的应用镜像,也掌握了在容器内进行临时操作和问题排查的技巧。
理解这两者的区别和联系,是掌握 Docker 自主权的关键。记住核心原则: 固化环境用 Dockerfile,临时调整进容器。
为了让你构建的镜像更专业、更高效,这里有几个进阶方向:
- 多阶段构建 (Multi-stage Build) :对于需要编译的应用(如 Go、Java),可以在一个镜像中编译,在另一个更小的镜像中运行,从而极大减小最终镜像体积。
- 使用 Docker Compose :当你需要定义和运行多个相关联的容器(如一个 Web 应用 + 一个数据库)时,
docker-compose.yml文件能帮你一键管理整个应用栈。 - 镜像安全扫描 :使用
docker scan或集成工具对构建的镜像进行漏洞扫描,确保交付物的安全性。 - 搭建私有镜像仓库 :使用 Harbor 或 Docker Registry 在企业内部搭建私有的镜像仓库,用于存储和分发自定义镜像。
Docker 的世界远不止于此,但扎实的基础是探索一切高级特性的前提。建议你将本文中的示例代码亲手运行一遍,并尝试修改 Dockerfile 和容器内的操作,将其应用到你的实际项目中。当你成功将自己的第一个服务封装进 Docker 镜像时,你会对“开发与运维的一致性”有更深的理解。
更多推荐
所有评论(0)