一、自定义镜像docker commit
1.1 镜像构建原因
在现代软件开发和运维中,Docker 已成为一种非常流行的工具,它通过容器化应用程序来简化部署过程。
然而,默认的官方镜像往往只能满足基础需求,无法涵盖所有特定项目的具体要求。因此,构建自己的 Docker 镜像是非常必要的。
你负责维护一个线上服务,使用的是基于
nginx
的镜像,运行着一个前端静态站点,并配置了复杂的 Nginx 路由、HTTPS 证书和访问控制策略。现在公司要进行一次 灾备演练,要求你在不依赖线上环境的情况下,在测试集群中快速还原出一个“与生产几乎一致”的服务实例,用于模拟故障切换。
在某些场景下,确实不需要构建镜像
尤其是满足以下条件时:
所有配置文件都通过
volume
挂载应用数据也通过
volume
存储容器本身只是运行时环境,没有额外的修改
镜像存在的意义,是在什么情况下不可替代?
你负责一个部署内部服务,该服务基于
nginx:1.21-alpine
运行,但为了满足公司安全合规要求,你需要:
在容器中安装审计工具(如
auditd
,syslog
,osquery
)修改默认 root 用户为非 root 用户
设置 SELinux 或 AppArmor 策略
删除一些不必要的系统命令(如
telnet
,nc
,tcpdump
)强化日志输出格式并集成到统一日志平台
这些操作不能通过 volume 实现,因为它们涉及到:
容器内系统组件的变更
用户权限、安全策略等底层设置
系统二进制文件的删除或替换
完成这些操作后,希望把当前容器的状态保存下来,用于后续所有节点统一部署。
原因 | 说明 |
---|---|
系统级改动无法通过 volume 实现 | 修改用户、删除命令、安装系统组件等只能在镜像层完成 |
安全加固要求可复制、可验证 | 你不能让每个节点手动执行一遍加固脚本,必须统一标准 |
合规性要求可追溯 | 构建镜像可以打标签、记录作者和时间,方便审计和版本管理 |
镜像 vs Volume 的适用场景对比表
场景 | 是否需要构建镜像 | 使用 Volume 是否足够 |
---|---|---|
修改系统组件(如安装包、删命令) | 必须构建镜像 | 不够 |
用户权限、安全策略调整 | 必须构建镜像 | 不够 |
日志配置、访问控制等应用级配置 | 可以用 volume | 足够 |
数据持久化、网站静态资源 | 可以用 volume | 足够 |
安全合规、标准化部署 | 必须构建镜像 | 不够 |
Volume 是用来存放“可变的业务数据”,而镜像则是用来固化“不变的运行环境”。
构建 Docker 镜像的方式
编号 | 构建方式 | 说明 |
---|---|---|
1 | docker commit (容器提交) | 基于运行中的容器状态生成镜像 |
2 | Dockerfile + docker build | 通过编写 Dockerfile 文件定义镜像内容,并使用 docker build 构建 |
1.2 docker commit
docker commit
是一个用于将容器的当前状态保存为新镜像的命令。
这种方法非常适合快速测试或对现有镜像进行小范围的修改。
然而,它不推荐用于复杂的或需要长期维护的应用场景,因为它的透明度较低、不可重复性高,并且难以版本控制和团队协作。
1.3 自定义 HTTP 镜像
在 CentOS 7 容器中安装 httpd
,并提交为新镜像 my-httpd-image
启动容器并进入交互式 shell
docker run -it --name my-httpd-container centos:7 /bin/bash
这会创建一个名为 my-httpd-container
的容器,并进入它的 bash 环境。
在容器内安装 httpd
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 httpd
退出容器
exit
提交容器为新镜像
docker commit [容器名] 镜像名:[版本号]
1.4 自定义 Nginx 镜像
在 CentOS 7 容器中安装 nginx
,并提交为新镜像 my-nginx-image
启动容器并进入交互式 shell
docker run -it --name v1 yum:1.0 /bin/bash
添加 EPEL 仓库并安装 nginx
CentOS 7 默认仓库中没有 nginx,需要先添加 EPEL 仓库:
yum install -y epel-release
yum install -y nginx
修改默认页面
同样地,可以修改默认首页内容:
echo "Hello from Nginx in Docker" > /usr/share/nginx/html/index.html
提交容器为新镜像
docker commit v1 mynginx:1.0
二、自定义镜像dockerfile
2.1 基础知识
Dockerfile 是一个文本文件,包含了一系列用于构建 Docker 镜像的 指令(Instructions)。
它类似于 shell 脚本,但不是直接运行命令,而是告诉 Docker 如何一步步构建出一个定制化的镜像。
一个完整的 Dockerfile 通常包含以下几个部分:
-
基础镜像
-
元数据信息
-
安装与配置
-
工作目录设置
-
文件复制
-
环境变量设置
-
端口暴露
-
启动命令
-
入口点
-
其他高级指令
2.2 指令详解
FROM
—— 指定基础镜像
作用:指定当前镜像构建所依赖的基础镜像。
格式:
FROM <image>:[版本号]
说明:
必须是 Dockerfile 中的第一个有效指令(除非是多阶段构建)。
可以选择官方镜像(如 nginx
, python
, alpine
)或你自己定义的镜像。
示例:
FROM ubuntu:20.04
FROM centos:7
LABEL
—— 添加元数据标签
作用:为镜像添加描述性信息,比如作者、版本等。 格式:
LABEL <key>=<value> ...
说明:
替代已废弃的 MAINTAINER
指令。
可以多次使用,也可以一次写多个键值对。
示例:
LABEL maintainer="john@example.com" \ 所属者
version="1.0" \ 版本
description="This is a custom Nginx image" 描述这个镜像是用来做什么的
RUN
—— 构建时执行命令
作用:在构建过程中执行命令,通常用于安装软件包等操作。
格式:
Shell 形式(隐式调用 /bin/sh -c
):
RUN command arg1 arg2
Exec 形式(推荐):
RUN ["command", "arg1", "arg2"]
说明:
每个 RUN
指令都会生成一个新的镜像层(layer)。
建议将多个命令合并到一行中,减少层数。
示例:
RUN yum update && \
yum install -y nginx
WORKDIR
—— 设置工作目录
作用:设置后续指令(如 COPY
, ADD
, CMD
, ENTRYPOINT
)的工作目录。
格式:
WORKDIR /path/to/workdir
说明:
如果目录不存在,会自动创建。
推荐使用绝对路径。
示例:
WORKDIR /app
COPY
和 ADD
—— 复制文件到镜像中
COPY
作用:从本地主机复制文件/目录到镜像中。
优点:简单、安全、推荐使用。
格式:
COPY <src>... <dest>
示例:
COPY ./index.html /usr/share/nginx/html/index.html
ADD
作用:功能类似 COPY
,但还能自动解压 tar 文件、支持远程 URL 下载。
示例:
ADD app.tar.gz /opt/app/
ADD https://example.com/file.txt /tmp/
推荐:优先使用
COPY
,只有必要时才用ADD
。
ENV
—— 设置环境变量
作用:设置容器内使用的环境变量。
格式:
ENV <key>=<value> ...
说明:
在后续指令中可以通过 $VAR_NAME
引用。
容器启动后也会保留这些变量。
示例:
ENV APP_HOME=/var/www/app \
NODE_ENV=production
EXPOSE
—— 声明容器监听端口
作用:声明容器在运行时会监听哪些端口。
格式:
EXPOSE <port>[/<protocol>]...
说明:
不会自动映射宿主机端口,只是文档性质的说明。
实际映射需在 docker run
时通过 -p
参数指定。
示例:
EXPOSE 80/tcp
EXPOSE 8000
示例运行时映射:
docker run -d -p 8080:80 my-nginx-image
CMD
—— 容器启动时默认执行的命令
作用:定义容器启动时默认执行的命令。
格式:
Exec 形式(推荐):
CMD ["command", "arg1", "arg2"]
Shell 形式(隐式调用 /bin/sh -c
):
CMD command arg1 arg2
说明:
可以被 docker run
命令覆盖。
一个 Dockerfile 中只能有一个有效的 CMD
(如果有多个,最后一个生效)。
示例:
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT
—— 容器入口点(固定命令)
作用:设置容器启动时必须运行的命令,比 CMD
更“固定”。 格式:
ENTRYPOINT ["command", "param1", "param2"]
说明:
与 CMD
配合使用时,CMD
的内容作为参数传递给 ENTRYPOINT
。
不能轻易被 docker run
覆盖(除非加 --entrypoint
)。
示例:
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
指令 | 是否可省略 | 是否影响运行 | 是否可覆盖 | 典型用途 |
---|---|---|---|---|
FROM | ❌ | ✅ | ❌ | 指定基础镜像 |
LABEL | ✅ | ❌ | ❌ | 添加元数据 |
RUN | ✅ | ✅ | ❌ | 安装软件 |
CMD | ✅ | ✅ | ✅ | 设置默认启动命令 |
ENTRYPOINT | ✅ | ✅ | ❌(需特殊参数) | 固定入口命令 |
EXPOSE | ✅ | ❌ | ❌ | 声明监听端口 |
ENV | ✅ | ✅ | ❌ | 设置环境变量 |
COPY | ✅ | ✅ | ❌ | 复制文件 |
ADD | ✅ | ✅ | ❌ | 类似 COPY,支持 tar 和 URL |
WORKDIR | ✅ | ✅ | ❌ | 设置工作目录 |
2.3 构建 httpd 镜像
mkdir /test
cd /test
vim dockerfile
-- 将下面的内容放入
# 使用官方 CentOS 7 镜像作为基础镜像
FROM centos:7
# 添加元数据信息
LABEL maintainer="admin" \
description="CentOS 7"
# 备份原始的 CentOS-Base.repo
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
# 使用 curl 下载阿里云的 CentOS Base.repo 文件
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
# 清理并重建缓存
RUN yum clean all && yum makecache
# 安装一个httpd
RUN yum install -y httpd
EXPOSE 80
# 设置容器启动命令(示例)
CMD ["httpd", "-D", "FOREGROUND"]
构建 Docker 镜像
打开终端或命令行工具,导航到包含 Dockerfile 的目录,然后运行以下命令来构建 Docker 镜像。
请将 <your-image-name>
替换为你想要给这个镜像起的名字:
docker build -t <your-image-name> .
2.4 构建 nginx 镜像
# 使用官方 CentOS 7 镜像作为基础镜像
FROM centos:7
# 添加元数据信息
LABEL maintainer="admin" \
description="CentOS 7"
# 备份原始的 CentOS-Base.repo
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
# 使用 curl 下载阿里云的 CentOS Base.repo 文件
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
# 清理并重建缓存
RUN yum clean all && yum makecache
# 安装 EPEL 源(Nginx 在 EPEL 中)
RUN yum install -y epel-release && \
yum clean all && \
yum makecache
# 安装 Nginx
RUN yum install -y nginx && \
yum clean all
# 创建存放网页内容的目录
RUN mkdir -p /var/www/html
# 将HTML压缩包复制到容器中
COPY html.zip /tmp/html.zip
# 安装 unzip 工具
RUN yum install -y unzip && \
yum clean all
# 解压HTML内容到指定目录
RUN unzip /tmp/html.zip -d /var/www/html/ && \
rm /tmp/html.zip
# 修改 Nginx 默认配置文件,将根目录改为 /var/www/html
RUN sed -i 's|/usr/share/nginx/html|/var/www/html|g' /etc/nginx/nginx.conf
# 暴露80端口
EXPOSE 80
# 启动 Nginx 服务(前台模式)
CMD ["nginx", "-g", "daemon off;"]
测试:
docker run -d name n1 my-nginx
docker inspect n1 看它的ip
curl ip
2.5 构建服务镜像
FROM centos:7
LABEL maintainer="xyz" \
description="CentOS 7 with Java application"
WORKDIR /usr/local/java
ADD jre-8u221-linux-x64.tar.gz ./
COPY msg.jar ./app.jar
ENV JAVA_HOME=/usr/local/java/jre1.8.0_221
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]