Docker镜像定制与容器内服务部署实战指南
在容器化技术普及的今天Docker 已成为开发、测试和部署的基石。然而很多开发者在使用 Docker 时往往停留在docker run和docker pull的层面一旦需要定制化环境或在容器内部署服务就感到无从下手。你是否遇到过这些问题想基于一个基础镜像安装特定软件却不知道如何构建自己的镜像需要在容器内调试或安装服务却发现没有yum或apt可用或者你希望将一个完整的应用如 Nginx、MySQL封装进镜像实现一键部署本文将系统性地解决这些痛点手把手带你从零开始掌握定制 Docker 镜像、为容器配置 Yum 仓库并在容器内完成服务安装部署的全流程。无论你是刚接触 Docker 的新手还是希望深化容器化实践的开发者这篇实战指南都将提供可直接复用的代码和清晰的排错思路。1. 核心概念与背景为什么需要定制镜像与容器内操作在深入实操之前我们有必要厘清几个核心概念理解“为什么”要这么做这比单纯知道“怎么做”更重要。Docker 镜像是一个轻量级、可执行的独立软件包它包含运行某个软件所需的所有内容代码、运行时、库、环境变量和配置文件。你可以把它理解为一个应用程序及其运行环境的“模板”或“快照”。Docker 容器是镜像的运行实例。当你运行一个镜像时Docker 会创建一个隔离的进程空间这个空间就是容器。容器与宿主机共享内核但拥有独立的文件系统、网络和进程空间。那么为什么我们需要定制镜像而不是直接使用官方镜像呢环境一致性官方镜像如centos:7通常只包含最精简的系统。你的应用可能需要特定的依赖库、配置文件或环境变量。通过定制镜像可以确保任何从该镜像启动的容器都拥有完全一致的环境彻底杜绝“在我机器上能跑”的问题。部署效率将应用及其环境打包成一个镜像后部署就变成了简单的docker run或docker-compose up无需在目标服务器上重复执行复杂的安装和配置步骤。版本控制与回滚镜像可以通过标签进行版本管理。如果新版本的应用出现问题可以立即回滚到旧版本的镜像实现快速恢复。而在容器内配置 Yum 仓库并安装服务则是在两种常见场景下的必备技能场景一镜像构建过程。在编写Dockerfile构建镜像时我们经常需要通过包管理器如 CentOS 的 Yum、Ubuntu 的 Apt安装软件。场景二容器运行时调试与临时变更。对于正在运行的容器我们可能需要临时进入其中安装一些调试工具如vim,net-tools,curl或排查问题这时就需要在容器内配置软件源并安装。本文将围绕这两个核心技能展开通过完整的示例让你不仅能构建出属于自己的镜像还能灵活应对容器内的各种环境配置挑战。2. 环境准备与版本说明在开始之前请确保你有一个可用的 Docker 环境。本文将基于 Linux 环境进行演示但核心概念和命令在 Windows 和 macOS 的 Docker Desktop 上同样适用。基础环境要求操作系统任何支持 Docker 的 Linux 发行版如 CentOS 7/8, Ubuntu 20.04/22.04、Windows 10/11需启用 WSL2或 macOS。Docker 引擎版本 20.10.0 或更高。本文示例命令在 Docker 23.0 版本测试通过。检查与安装 Docker如果你的系统尚未安装 Docker可以参考以下最简洁的步骤进行安装以 CentOS/RHEL 系列为例卸载旧版本如有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设置稳定的镜像仓库这里使用阿里云镜像加速sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装 Docker 引擎# 安装最新版本 sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 或者安装特定版本先列出可用版本 # yum list docker-ce --showduplicates | sort -r # sudo yum install docker-ce-VERSION_STRING docker-ce-cli-VERSION_STRING containerd.io docker-buildx-plugin docker-compose-plugin启动并设置开机自启sudo systemctl start docker sudo systemctl enable docker验证安装sudo docker run hello-world如果能看到 “Hello from Docker!” 等欢迎信息说明 Docker 已安装并运行成功。本文涉及的镜像与软件版本基础镜像我们将使用centos:7作为示例这是一个非常常见的起点。其对应的 Docker Hub 页面会提供具体的版本信息。软件包在容器内我们将演示安装nginxWeb 服务器和vim编辑器等常用软件。Yum 仓库我们将把阿里云的 CentOS 镜像源配置到容器内以获得更快的下载速度。重要提示Docker 和软件版本迭代较快本文的命令和配置思路是通用的。在实际操作时请务必根据你使用的具体基础镜像版本如ubuntu:22.04和所需的软件版本进行调整。核心在于掌握方法而非死记命令。3. 实战一使用 Dockerfile 定制简单镜像Dockerfile 是一个文本文件里面包含了一系列的指令用于指导 Docker 如何自动构建镜像。这是定制镜像的标准和推荐方式。3.1 Dockerfile 核心指令解析在编写 Dockerfile 之前先了解几个最关键的指令FROM指定基础镜像所有构建都由此开始。例如FROM centos:7。RUN在镜像构建过程中执行命令。常用于安装软件包、创建目录等。例如RUN yum install -y nginx。COPY或ADD将文件从构建上下文你运行docker build命令的目录复制到镜像中。WORKDIR设置工作目录。后续的RUN,CMD,ENTRYPOINT,COPY等指令都会在这个目录下执行。EXPOSE声明容器运行时监听的端口。这只是一个元数据方便使用者了解并不会自动打开端口。CMD或ENTRYPOINT指定容器启动时默认执行的命令。一个 Dockerfile 中只能有一个CMD如果有多个只有最后一个生效ENTRYPOINT通常与CMD结合使用。3.2 完整示例构建一个包含 Nginx 的 CentOS 镜像我们的目标是创建一个基于centos:7的镜像并在其中安装 Nginx同时将我们的自定义首页复制进去。步骤 1创建项目目录和文件在本地创建一个工作目录并进入mkdir my-nginx-image cd my-nginx-image步骤 2创建自定义首页文件创建一个简单的index.html文件cat index.html EOF !DOCTYPE html html head titleMy Custom Nginx in Docker/title /head body h1Hello from my custom Docker image!/h1 pThis page is served by Nginx inside a container./p /body /html EOF步骤 3编写 Dockerfile创建Dockerfile文件注意没有后缀名# 使用 CentOS 7 作为基础镜像 FROM centos:7 # 维护者信息可选 LABEL maintaineryour-emailexample.com # 设置工作目录后续操作默认在此目录下进行 WORKDIR /usr/share/nginx/html # 安装 EPEL 仓库Extra Packages for Enterprise Linux以获取更多软件包 # 然后安装 nginx RUN yum install -y epel-release \ yum install -y nginx \ yum clean all # 将本地的 index.html 文件复制到镜像的工作目录覆盖默认首页 COPY index.html /usr/share/nginx/html/ # 暴露 Nginx 默认的 80 端口 EXPOSE 80 # 设置容器启动时执行的命令启动 Nginx 并以非守护进程模式运行 CMD [nginx, -g, daemon off;]关键点解释RUN yum install -y epel-release \ yum install -y nginx \ yum clean all这里使用了将多个命令连接在同一个RUN指令中并用\换行以提高可读性。这样做的好处是减少镜像层数每个RUN、COPY等指令都会创建一层并且最后执行yum clean all可以清理缓存减小最终镜像的体积。CMD [nginx, -g, daemon off;]这是启动 Nginx 的典型方式。-g “daemon off;”让 Nginx 在前台运行这对于 Docker 容器是必需的因为容器需要有一个持续运行的前台进程才能保持运行状态。步骤 4构建镜像在Dockerfile所在的目录即my-nginx-image执行构建命令docker build -t my-custom-nginx:1.0 .-t my-custom-nginx:1.0为构建的镜像打上标签名称是my-custom-nginx标签是1.0。标签常用于区分版本。.这个点代表构建上下文的路径Docker 引擎会将这个目录下的所有文件发送给 Docker 守护进程。Dockerfile和index.html都需要在这个目录下。构建过程中你会看到 Docker 一步步执行Dockerfile中的指令。构建成功后可以使用docker images查看本地镜像列表应该能看到my-custom-nginx。步骤 5运行容器并测试运行基于我们自定义镜像的容器docker run -d -p 8080:80 --name my-nginx-container my-custom-nginx:1.0-d后台运行容器。-p 8080:80端口映射将宿主机的 8080 端口映射到容器的 80 端口。--name my-nginx-container为容器指定一个名称。现在打开你的浏览器访问http://localhost:8080如果 Docker 在远程服务器则替换为服务器 IP你应该能看到我们自定义的 HTML 页面。步骤 6验证与清理# 查看正在运行的容器 docker ps # 查看容器日志 docker logs my-nginx-container # 停止容器 docker stop my-nginx-container # 删除容器 docker rm my-nginx-container # 可选删除镜像 # docker rmi my-custom-nginx:1.0通过这个完整的流程你已经成功定制了第一个 Docker 镜像。接下来我们看另一种场景对正在运行的容器进行操作。4. 实战二进入运行中的容器并配置 Yum 仓库有时我们并不想或不需要构建一个新镜像只是想在某个正在运行的容器里临时安装一些工具如vim,net-tools,telnet来进行调试。这时我们就需要进入容器内部操作。4.1 进入容器的几种方式docker exec推荐在已运行的容器中执行命令。这是最常用、最灵活的方式。# 启动一个基础的 CentOS 容器并后台运行 docker run -d --name my-centos centos:7 tail -f /dev/null # 使用 exec 进入容器的交互式 bash shell docker exec -it my-centos /bin/bash-i保持标准输入打开允许交互。-t分配一个伪终端pseudo-TTY这样我们才能使用 bash。/bin/bash要执行的命令这里就是启动一个 bash shell。docker attach不推荐用于此场景连接到正在运行的容器的主进程的标准输入、输出和错误流。如果主进程是 shell那看起来和exec类似。但关键区别是如果你从attach的终端退出按CtrlC或输入exit可能会导致容器停止运行因为它终止了主进程。而exec启动的是新的进程退出不会影响容器主进程。4.2 在容器内配置 Yum 仓库以阿里云镜像为例默认的centos:7镜像非常精简连vim和wget都没有。而且其自带的 Yum 源可能速度较慢。我们进入容器后第一步就是更换为国内的镜像源。步骤 1进入容器docker exec -it my-centos /bin/bash成功执行后你的命令行提示符会变成类似[root容器ID /]#表示你已经在容器内部。步骤 2备份原有 Yum 源配置文件好习惯cd /etc/yum.repos.d/ mkdir bak mv *.repo bak/步骤 3下载阿里云的 CentOS 7 镜像源配置文件由于容器内可能没有wget或curl我们可以用yum先安装其中一个或者直接从宿主机构建上下文复制。这里演示安装wget如果基础源还能用的话# 先尝试用基础源安装 wget如果失败说明基础源不可用需要手动创建 repo 文件 yum install -y wget如果安装成功则下载阿里云 repo 文件wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo如果yum install失败很常见我们需要手动创建 repo 文件。退出容器exit在宿主机上创建一个centos-7-aliyun.repo文件内容如下[base] nameCentOS-$releasever - Base - mirrors.aliyun.com failovermethodpriority baseurlhttp://mirrors.aliyun.com/centos/$releasever/os/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/os/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/os/$basearch/ gpgcheck1 gpgkeyhttp://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 [updates] nameCentOS-$releasever - Updates - mirrors.aliyun.com failovermethodpriority baseurlhttp://mirrors.aliyun.com/centos/$releasever/updates/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/updates/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/updates/$basearch/ gpgcheck1 gpgkeyhttp://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 [extras] nameCentOS-$releasever - Extras - mirrors.aliyun.com failovermethodpriority baseurlhttp://mirrors.aliyun.com/centos/$releasever/extras/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/extras/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/extras/$basearch/ gpgcheck1 gpgkeyhttp://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 [centosplus] nameCentOS-$releasever - Plus - mirrors.aliyun.com failovermethodpriority baseurlhttp://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/centosplus/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/centosplus/$basearch/ gpgcheck1 gpgkeyhttp://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7然后将其复制到容器内# 假设文件在宿主机当前目录 docker cp centos-7-aliyun.repo my-centos:/etc/yum.repos.d/ # 再次进入容器 docker exec -it my-centos /bin/bash # 在容器内将复制的文件重命名为 CentOS-Base.repo或者直接使用 mv /etc/yum.repos.d/centos-7-aliyun.repo /etc/yum.repos.d/CentOS-Base.repo步骤 4清理并重建 Yum 缓存yum clean all yum makecache步骤 5测试安装软件现在你可以安装任何需要的软件了速度会快很多。yum install -y vim net-tools telnet tree重要提示通过docker exec在容器内所做的任何修改包括安装软件、修改配置都只存在于这个容器层。如果这个容器被删除所有修改都会丢失。这就是为什么对于持久化的环境变更我们推荐使用Dockerfile来构建镜像。5. 实战三在容器内安装部署服务以 Nginx 为例结合前两部分我们现在完成一个更综合的任务不通过Dockerfile预先构建而是直接在一个基础 CentOS 容器内“手动”安装并配置 Nginx 服务并使其运行。这个实战模拟了这样一种场景你拿到一个纯净的基础镜像容器需要将其配置成一个可用的应用服务器。步骤 1启动一个干净的 CentOS 容器我们使用-it参数直接以交互模式启动并映射宿主机的 8081 端口到容器的 80 端口。docker run -it -p 8081:80 --name manual-nginx centos:7 /bin/bash这个命令会直接让你进入容器的 bash shell。步骤 2在容器内配置 Yum 仓库并安装 Nginx参照4.2节的方法在容器内配置阿里云 Yum 源。这里给出快速命令序列假设你已经手动复制了 repo 文件或网络通畅# 1. 进入 yum 源目录并备份 cd /etc/yum.repos.d mkdir -p bak mv *.repo bak/ 2/dev/null || true # 忽略错误 # 2. 安装 wget如果基础源可用否则需要宿主机 docker cp repo 文件进来 # 这里假设可以安装 yum install -y wget # 3. 下载阿里云 CentOS 7 repo 文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo # 4. 清理并重建缓存 yum clean all yum makecache # 5. 安装 EPEL 和 Nginx yum install -y epel-release yum install -y nginx步骤 3创建自定义 Nginx 首页并修改配置# 1. 创建自定义首页 echo h1Nginx Manually Installed in Container!/h1 /usr/share/nginx/html/index.html # 2. 可选修改 Nginx 配置例如修改默认端口或 server_name # vi /etc/nginx/nginx.conf # 本例中我们使用默认配置步骤 4启动 Nginx 服务在 CentOS 7 中我们可以使用systemctl命令但在 Docker 容器中systemd通常不运行。更简单直接的方式是运行 Nginx 可执行文件。# 直接前台启动 Nginx nginx -g “daemon off;”此时Nginx 会在前台启动并占用当前的终端。你可以在宿主机打开另一个终端窗口进行测试。步骤 5在宿主机测试服务在宿主机上使用curl或浏览器访问http://localhost:8081应该能看到 “Nginx Manually Installed in Container!” 的字样。curl http://localhost:8081步骤 6容器管理现在你面临一个问题Nginx 在前台运行占用了你当前的终端。如果你想退出终端但保持容器运行需要以后台模式启动 Nginx。在当前容器终端按CtrlC停止 Nginx 进程。以后台模式启动 Nginx并退出容器# 在容器内执行 nginx # 然后按 CtrlP, 再按 CtrlQ 来 detach分离容器终端让容器在后台运行。 # 或者直接退出 bash但这样会导致容器停止因为 bash 是主进程。 # 更好的方式是在启动容器时就指定 nginx 为启动命令。这个“手动安装”的过程清晰地展示了容器内服务部署的步骤但它不是持久化的。一旦容器删除一切都需要重来。因此对于生产环境我们总是将这类步骤写入Dockerfile。6. 将手动步骤转化为 Dockerfile最佳实践让我们把实战三中的手动操作固化到一个Dockerfile中形成可重复构建的镜像。创建一个新的目录manual-nginx-dockerfile和文件# Dockerfile FROM centos:7 LABEL maintainerdevexample.com # 1. 配置阿里云 Yum 源 RUN cd /etc/yum.repos.d/ \ mkdir -p bak \ mv *.repo bak/ 2/dev/null || true \ curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \ yum clean all \ yum makecache # 2. 安装 EPEL 和 Nginx RUN yum install -y epel-release \ yum install -y nginx \ yum clean all # 3. 创建自定义首页 RUN echo “h1Nginx Installed via Dockerfile!/h1” /usr/share/nginx/html/index.html # 4. 暴露端口 EXPOSE 80 # 5. 启动命令 CMD [nginx, -g, daemon off;]构建并运行docker build -t nginx-from-dockerfile . docker run -d -p 8082:80 --name nginx-df nginx-from-dockerfile访问http://localhost:8082验证成功。现在任何人拿到这个Dockerfile都能构建出一模一样的镜像这就是 Dockerfile 的价值。7. 常见问题与排查思路在定制镜像和操作容器的过程中你可能会遇到以下问题问题现象可能原因排查思路与解决方案docker build失败提示yum install超时或找不到包。1. 容器内无法访问外网。2. 基础镜像的 Yum 源配置有问题或已过期。1. 检查宿主机的网络确保容器有网络访问权限默认桥接网络通常可以。2. 在Dockerfile的RUN指令中第一步就配置国内镜像源如阿里云、清华源。3. 对于centos:7等较老镜像其内置的源可能已失效必须在安装任何软件前先更新源。容器启动后立即退出。容器内没有持续运行的前台进程。Docker 容器要求至少有一个前台进程否则会认为任务完成而退出。1. 检查Dockerfile中的CMD或ENTRYPOINT指令是否正确。对于 Nginx、Tomcat 等通常需要以前台模式启动如nginx -g ‘daemon off;’。2. 使用docker logs 容器名查看容器退出前的日志寻找错误信息。进入容器后systemctl命令无法使用。大多数基础镜像为了精简不包含systemd初始化系统。1.不要在容器内使用systemctl管理服务。直接运行服务的可执行文件如/usr/sbin/nginx。2. 如果确实需要systemd可以考虑使用centos:7-systemd等特殊镜像但这违背了容器轻量化的原则不推荐。在容器内安装的软件重启容器后丢失。对运行中容器的修改仅存在于可写容器层未提交为新的镜像层。1.临时调试这种丢失是正常的容器本就是无状态的。2.持久化变更使用docker commit 容器ID 新镜像名:标签将当前容器状态保存为新镜像。但这并非最佳实践因为过程不透明、不可重复。强烈推荐使用Dockerfile来记录所有构建步骤。宿主机无法通过映射端口访问容器内的服务。1. 容器内服务未监听在正确端口如0.0.0.0。2. 防火墙宿主机或云服务商阻止了端口访问。3.docker run -p参数映射错误。1. 进入容器 (docker exec)使用netstat -tlnp或ss -tlnp检查服务是否在运行并监听在预期的端口如 80。2. 检查宿主机防火墙规则firewall-cmd --list-all或iptables -L。3. 确认docker run命令的-p参数格式为宿主机端口:容器端口。docker cp复制文件到容器时提示错误。1. 容器不存在或未运行。2. 容器内的目标路径不存在。1. 使用docker ps确认容器名称或 ID 正确且容器正在运行。2. 对于目标路径可以先进入容器创建目录或者使用绝对路径。8. 最佳实践与工程建议掌握基础操作后遵循以下最佳实践能让你的 Docker 使用更高效、更安全。使用.dockerignore文件在构建镜像的目录下创建.dockerignore文件列出不需要发送给 Docker 守护进程的文件和目录如.git,node_modules, 日志文件等。这可以显著减少构建上下文大小加速构建过程。**/.git **/node_modules **/*.log **/target Dockerfile* docker-compose* README.md优化 Dockerfile多阶段构建对于需要编译的应用如 Go、Java使用多阶段构建可以极大减小最终镜像体积。在一个阶段编译在另一个只包含运行环境的阶段复制编译好的二进制文件。合并 RUN 指令将多个RUN指令用连接减少镜像层数。合理安排指令顺序将变化频率低的指令如安装基础软件包放在前面变化频率高的指令如复制应用代码放在后面。这样可以充分利用 Docker 的构建缓存。镜像标签与版本管理始终为镜像打上有意义的标签如myapp:1.2.3,myapp:latest。在生产环境中避免使用latest标签因为它会指向不确定的版本。使用具体的版本号或 Git 提交哈希。考虑使用私有镜像仓库如 Harbor、AWS ECR、阿里云 ACR来存储和管理镜像。容器内操作的安全与清洁在Dockerfile的RUN指令中安装完软件后记得运行yum clean all(apt-get clean) 来清理缓存减小镜像体积。避免在容器中以 root 用户运行应用。在Dockerfile中使用USER指令切换到非 root 用户。不要将敏感信息如密码、密钥硬编码在Dockerfile或镜像中。使用环境变量或 Docker 密钥管理功能在运行容器时注入。从容器内安装到 Dockerfile 构建的思维转变调试时可以docker exec进入容器操作。一旦确定了环境配置和软件安装步骤立即将其转化为Dockerfile。这是实现基础设施即代码和持续集成/持续部署的基础。通过本文的讲解你应该已经掌握了 Docker 镜像定制和容器内服务部署的核心技能。从理解概念到手动操作再到通过 Dockerfile 实现自动化这是一个循序渐进的过程。真正的熟练来自于实践建议你从为一个简单的个人项目编写 Dockerfile 开始逐步尝试更复杂的多服务应用并探索 Docker Compose 和 Kubernetes 等编排工具从而构建起完整的容器化开发和部署能力。