Docker--Docker镜像制作的注意事项

Docker 镜像制作 dockerfiles的指令讲解 链接

CMD和ENTRYPOINT

CMDENTRYPOINTDockerfile 中用于指定容器启动时运行命令的两个指令。它们在功能上有一些相似之处,但也存在重要区别。

在编辑Dockerfile时,ENTRYPOINT 或者CMD 命令会自动覆盖之前的ENTRYPOINT 或者CMD 命令,但是ENTRYPOINTCMD不会相互覆盖。

如果容器启动时通过 docker run 提供了命令行参数,CMD 的内容会被覆盖,而 ENTRYPOINT 的内容可以作为基础命令,并接收 CMD 或运行参数作为附加参数。

下面通过实例来进行讲解:

编辑Dockerfile1文件

cpp 复制代码
FROM busybox
CMD echo "test cmd1"
CMD echo "TEST CMD1"

创建镜像并运行容器

bash 复制代码
docker build -t cmd:0.1 -f ./Dockerfile1 .
docker run --name cmd1 --rm cmd:0.1 
TEST CMD1

编辑Dockerfile2文件

c 复制代码
FROM busybox
ENTRYPOINT echo "test cmd1"
ENTRYPOINT echo "TEST CMD1"

创建镜像并运行容器

bash 复制代码
docker build -t cmd:0.2 -f ./Dockerfile2 .
docker run --name cmd1 --rm cmd:0.2 
TEST CMD1

编辑Dockerfile3文件

c 复制代码
FROM busybox
ENTRYPOINT echo "test cmd1"
CMD echo "TEST CMD1"

创建镜像并运行容器

bash 复制代码
docker build -t cmd:0.3 -f ./Dockerfile3 .
docker run --name cmd1 --rm cmd:0.3 
test cmd1

执行指令不会被覆盖,但只是执行一条语句

编辑Dockerfile0文件

c 复制代码
FROM ubuntu:latest
ENTRYPOINT ["echo"]
CMD ["Hello from CMD"]

启动容器时,默认执行 echo "Hello from CMD"

如果运行 docker run myimage "Hello from run",输出 Hello from run,因为 CMD 的内容被覆盖,但 ENTRYPOINT 仍然是 echo

Shell形式和exec形式

exec形式:

c 复制代码
ENTRYPOINT ["executable", "param1", "param2"]

Shell形式:

bash 复制代码
ENTRYPOINT command param1 param2

当使用shell表示法时,命令行程序作为sh程序的子程序运行;docker用bin/sh -c来调用

当用docker ps 查看时就能看出我们运行的是/bin/sh -c 的命令;这样运行的PID不是1;这也意味着Unix不会接收任何信号;

exec语法中,没有启动/bin/sh的命令,而是直接运行提供的命令,命令PID是1.

编辑dockerfile4文件

shell模式

c 复制代码
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ping localhost

创建镜像并运行容器:

bash 复制代码
docker build -t cmd:0.4 -f ./Dockerfile4 .
docker run -d --name cmd1 --rm cmd:0.4
docker ps

查看容器可以看到命令为 /bin/sh -c为主程序

进入容器内进行查看,可以看到PID1是/bin/sh

bash 复制代码
docker exec -it cmd1 bash
ps -ef

编辑dockerfile5文件

exec模式

c 复制代码
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ["ping","localhost"]

创建镜像并运行容器:

bash 复制代码
docker build -t cmd:0.5 -f ./Dockerfile5 .
docker run -d --name cmd1 --rm cmd:0.5

通过docker ps 查看:

进入到容器中查看

bash 复制代码
docker exec -it cmd1 bash
ps -ef

ENTRYPOINT也是一样的操作

组合模式

ENTRYPOINT可以作为默认的命令,CMD指定默认运行参数 ;

也就是说,docker会把CMD的命令拼接到ENTRYPOINT命令后面

编辑Dockerfie6文件内容:

c 复制代码
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
bash 复制代码
docker build -t cmd:0.6 -f ./Dockerfile6 .
docker run --name cmd1 --rm cmd:0.6
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from localhost (::1): icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from localhost (::1): icmp_seq=3 ttl=64 time=0.031 ms

可以看到CMD的内容被添加进去了;

CMD参数替换

bash 复制代码
docker run --name cmd1 --rm cmd:0.6 www.baidu.com

善用.dockerignore 文件

.dockerignore 文件是 Docker 构建镜像时的重要工具,用于指定在构建过程中应忽略的文件和目录。合理使用 .dockerignore 可以显著减少镜像体积、加快构建速度,并避免将敏感信息打包到镜像中。

默认情况下,Docker 会将 Dockerfile 所在目录的所有文件和子目录复制到构建上下文(build context)中。如果目录中包含大量无关文件(如日志文件、临时文件、IDE 配置文件等),会导致构建上下文过大,从而增加镜像体积。

构建上下文越大,Docker 在构建镜像时需要传输和处理的数据就越多,导致构建时间变长。通过忽略不必要的文件,可以显著缩短构建时间。

如果构建上下文中包含敏感文件(如 .env、密钥文件、配置文件等),可能会被意外打包到镜像中,导致安全风险。

.dockerignore 文件的语法与 .gitignore 类似,支持通配符和模式匹配。

每一行定义一个匹配规则,空行和以 # 开头的行会被忽略。

常见模式
*:匹配任意文件或目录(不包括目录本身)。
**:匹配任意层级的子目录。
?:匹配单个字符。
[abc]:匹配括号内的任意一个字符。
!:取反,表示不忽略匹配的文件。

如:

bash 复制代码
# 忽略所有日志文件
*.log

# 忽略临时文件
*.tmp
temp/

# 忽略特定目录
node_modules/
.git/

# 忽略所有隐藏文件(以.开头的文件)
.*

# 包含特定文件(即使它匹配了其他规则)
!important.txt

下面通过实例来验证:
建立dockerfile

c 复制代码
FROM centos:7

COPY ./* /

编写.dockerignore

c 复制代码
*.txt

创建几个文件

创建镜像

bash 复制代码
docker build -t test_ignore:1.0 ./

运行容器,然后查看目录内容

bash 复制代码
docker run  -it test_ignore:1.0

发现对应目录后缀txt文件都没有了

镜像的多阶段构建

多阶段构建是 Docker 提供的一种优化镜像构建流程的技术,允许在单个 Dockerfile 中使用多个 FROM 指令,每个 FROM 指令开启一个新的构建阶段。最终镜像只保留最后一个阶段的构建结果,从而大幅减小镜像体积,提升安全性和性能。

传统构建方式会将所有构建依赖(如编译器、构建工具)和中间产物打包到最终镜像中,导致镜像体积庞大。

构建工具和依赖可能包含已知漏洞,移除这些工具可以降低攻击面。

将构建逻辑集中在一个 Dockerfile 中,避免维护多个独立的构建脚本。

更小的镜像意味着更快的上传、下载和启动时间。

关键点 :
每个 FROM 指令开启一个新阶段。

阶段可以通过 AS关键字命名,方便后续引用。

使用 COPY --from= < stage > 从其他阶段复制文件。

实例:

编写dockerfile文件:

c 复制代码
#第一阶段构建
FROM centos:7 AS basebuilder

ENV VERSION="1.0"

RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
    -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
    -i.bak \
    /etc/yum.repos.d/CentOS-Base.repo

WORKDIR /src

COPY demo.c .

RUN yum makecache && yum install gcc -y

RUN gcc -o demo demo.c && \
    rm -f demo.c && \
    yum remove -y gcc

CMD ["/src/demo"]

我们将以这个镜像作为第一阶段的构建,编写第一阶段的Dockerfile,所占内存:

编写第二阶段的Dockerfile:

c 复制代码
#第一阶段构建
FROM centos:7 AS basebuilder

ENV VERSION="1.0"

RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
    -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
    -i.bak \
    /etc/yum.repos.d/CentOS-Base.repo

WORKDIR /src

COPY demo.c .

RUN yum makecache && yum install gcc -y

RUN gcc -o demo demo.c && \
    rm -f demo.c && \
    yum remove -y gcc

CMD ["/src/demo"]


# 第二阶段构建
FROM CentOS7
# 拷贝第一阶段生成的可执行程序
COPY --from=basebuilder /src/demo /src/demo
# 运行可执行程序
CMD ["/src/demo"]

domo文件:

c 复制代码
#include <stdio.h>

int main()
{
    printf("Test commit image\n");
    return 0;
}

创建镜像及运行容器

bash 复制代码
docker build -t multi:0.2 .
docker run --name multi --rm multi:0.2
Test commit image

查看镜像大小:

像我们这种只是跑一个可执行程序文件而已,如果我们将第二阶段基本镜像换成busybox的话,那么所占内存会更小:

bash 复制代码
docker build -t multi:0.3 .
docker run --name multi --rm multi:0.3
Test commit image

合理利用缓存

Docker 构建镜像时,默认会利用缓存机制加速构建过程:

如果某层(RUN、COPY、ADD等指令)的输入未变化,Docker 会直接复用缓存结果,跳过重复执行。
优势:显著减少构建时间,尤其是在依赖较多或构建步骤复杂时。

Docker 镜像由多个层(Layer)组成,每层对应一个构建指令。

缓存以层为单位存储,只有当某层的输入(如文件内容、环境变量)变化时,该层及其后续层才会失效。

缓存失效规则

COPY/ADD 指令

  • 如果源文件内容变化,缓存失效。
  • 如果仅文件名或目录结构变化,但内容未变,缓存可能仍有效(取决于文件哈希值)。

RUN 指令

  • 如果依赖的上一层缓存失效,则当前层的缓存也会失效。

环境变量

  • ARG 和 ENV 的变化会导致后续层缓存失效。

所以要利用好缓存的规则

  • 将不常变化的指令放在前面,常变化的指令放在后面
  • 避免在关键层使用 COPY
  • 使用 ARG 参数控制构建环境

实例:

利用c++制作的镜像Dockerfile文件进行使用:

dockerfile:

c 复制代码
FROM centos:7 AS basebuilder

ENV VERSION="1.0"

RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
    -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
    -i.bak \
    /etc/yum.repos.d/CentOS-Base.repo

RUN yum makecache && yum install gcc -y

WORKDIR /src

COPY demo.c .

RUN gcc -o demo demo.c && \
    rm -f demo.c && \
    yum remove -y gcc

CMD ["/src/demo"]

demo.c

c 复制代码
#include <stdio.h>

int main()
{
    printf("Test CACHE image\n");
    return 0;
}

第一次创建镜像

bash 复制代码
docker build -t cache:1.0 .

花费了40s的时间

第二次创建镜像之前,我们修改下demo.c:

再次创建镜像

bash 复制代码
docker build -t cache:2.0 .

会发现这次创建时间还是很多,这是因为demo.c的改变,影响了Dockerfile文件内容COPY demo.c . 后面全部内容,后面的内容都要进行重新创建;所以,安排好我们的执行语句顺序非常重要,将不常修改的语句放到前头,经常修改的语句放到后头;这里是我们安装gcc影响到了,但是对于我们来说又没有经常修改,所以我们将这条语句放到前面的位置

再次创建镜像

bash 复制代码
docker build -t cache:3.0 .                                                                                         
[+] Building 42.4s (11/11) FINISHED

由于我们改变了执行语句的顺序,所以需要重建

再次修改demo.c文件再创建镜像

bash 复制代码
docker build -t cache:4.0 .                                                                                         
[+] Building 1.9s (11/11) FINISHED
相关推荐
派阿喵搞电子8 分钟前
在Ubuntu下交叉编译 Qt 应用程序(完整步骤)
linux·运维·ubuntu
知北游天23 分钟前
Linux:基础IO---软硬链接&&动静态库前置知识
linux·运维·服务器
z日火34 分钟前
Windows Server 2019 安装 Docker 完整指南
windows·docker·容器
汤姆和杰瑞在瑞士吃糯米粑粑39 分钟前
【操作系统学习篇-Linux】进程
linux·运维·学习
邹卓为1 小时前
docker 安装 jenkins
java·docker·jenkins
清风~徐~来1 小时前
【Linux】进程创建、进程终止、进程等待
android·linux·运维
偏执的执1 小时前
linux常见命令
linux·运维·服务器
弧襪1 小时前
Docker Swarm 集群
docker·容器·eureka
就新年快乐吧2 小时前
【HD-RK3576-PI】Docker搭建与使用
linux·docker
AugustShuai2 小时前
服务器DNS失效
运维·服务器·dns·dns解析异常