发布时间:2024-12-13 人气:151次
Dockerfile介绍
1、Dockerfile简介2、Dockerfile构建镜像步骤3、Dockerfile指令4、案例
1.1
什么是Dockerfile之前的文章我们介绍了Docker的基础知识,这篇文章我们介绍Docker比较重要的知识点——Dockerfile。Dockerfile是一个用于构建Docker镜像的文本文件,包含了一系列指令和配置,这些指令定义了如何从一个基础镜像开始,通过添加、修改文件、安装软件包、配置环境变量和设置启动命令等操作,最终构建出一个新的Docker镜像。
1.2
为什么需要自定义镜像?我们知道Docker Hub官方提供了许多满足我们服务需求的镜像,基本包含了所有场景,那为什么还需要自定义镜像呢?
因为官方镜像可能无法完全满足特定应用的需求,或者需要对现有镜像进行扩展和优化。比如一下场景就需要我们自定义镜像:
特定需求配置:官方镜像通常提供通用的基础环境,但每个项目可能都有其特定的配置需求,如环境变量、配置文件等。通过自定义镜像,可以在构建过程中添加这些特定的配置,确保应用在部署时能够无缝运行。
持续集成/持续部署(CI/CD)流程集成:在CI/CD流程中,可能需要将应用打包成镜像,并在测试或生产环境中运行。通过自定义镜像,可以确保在不同环境中都能获得一致的构建结果,从而简化部署流程。
优化性能和资源利用:自定义镜像可以根据应用程序的需求进行优化,包括调整系统配置、优化启动脚本等,从而提高性能和资源利用率。通过减少不必要的软件包和依赖项,自定义镜像可以减小镜像大小,加快下载和启动速度。
......
1.3
Dockerfile原理谈到Dockerfile原理,首页我们得搞清楚Docker镜像的原理。先思考一下下面几个问题:1. docker镜像的本质是什么?
2. docker中一个centos镜像大约200M左右,为什么一个centos系统的iso安装文件要好几个G?
3. docker中一个tomcat镜像大约500M左右,为什么一个tomcat安装包不足100M呢?
通过上面的三个问题,衍生出来了一些知识点。操作系统是由:进程调度子系统、进程通信子系统、内存管理子系统、设备管理子系统、文件管理子系统、网络通信子系统、作业控制子系统组成。Linux的文件管理子系统由bootfs和rootfs组成:
bootfs:包含bootloader(引导加载程序)和 kernel(内核)
rootfs:root文件系统,包含的就是典型 Linux 系统中的/dev、/proc、/bin、/etc等标准目录和文件
(PS: 不同的linux发行版,bootfs基本一样,而rootfs不同,如ubuntu,centos等)
上面的知识点后,现在来看下镜像的原理:
Docker镜像是由特殊的文件系统叠加而成,最底端是 bootfs,并使用宿主机的bootfs;第二层是root文件系统rootfs,称为base image;然后再往上可以叠加其他的镜像文件。
统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。
一个镜像可以放在另一个镜像的上面。位于下面的镜像称为父镜像,最底部的镜像成为基础镜像。
当从一个镜像启动容器时,Docker会在最顶层加载一个读写文件系统作为容器。
现在我们就能来解答这三个问题了:Q1:docker 镜像的本质是什么?
A1:是一个分层的文件系统。
Q2:docker中一个centos镜像大约200M左右,为什么一个centos系统的iso安装文件要好几个G?
A2:centos的iso文件包括bootfs和rootfs,而docker的centos镜像复用操作系统的bootfs。
Q3:docker中一个tomcat镜像大约500M左右,为什么一个tomcat安装包不足100M呢?
A3:docker中的镜像是分层的,tomcat虽然只有70多M,但是它需要依赖父镜像和基础镜像,所有整个对外暴露的tomcat镜像大约500M左右。
2.1
Dockerfile文件是怎么生成镜像的?创建Dockerfile文件:首先,你需要创建一个包含Dockerfile指令的文本文件,并将其命名为“Dockerfile”。这个文件中将包含构建镜像所需的所有指令,例如基于哪个基础镜像、需要安装哪些软件包、需要复制哪些文件到镜像中等。
准备上下文(Context):Dockerfile通常与一个上下文一起使用,上下文是指在使用Docker构建镜像时,Docker引擎会将当前目录及其子目录中的所有文件发送到Docker守护进程,作为构建环境的一个上下文。简单来说,Docker build上下文就是构建镜像时,Docker引擎用来生成镜像的所有文件和目录的集合。在运行docker build命令时,需要指定上下文路径。
运行docker build命令:使用docker build命令来读取Dockerfile并构建镜像。命令的基本格式为:
docker bulid -t <镜像名>:<标签> <上下文路径>执行命令后:
读取 Dockerfile:Docker CLI会读取指定上下文路径中的Dockerfile。
创建基础镜像层:根据Dockerfile中的FROM指令拉取指定的基础镜像。
逐条执行指令:按照从上到下的顺序逐条执行Dockerfile中的指令。每条指令都会在镜像中创建一个新的层,并对镜像进行修改。例如,RUN指令会在容器中执行命令,COPY和ADD指令会将文件或目录复制到镜像中。
提交新镜像层:每执行完一条指令后,Docker会提交一个新的镜像层。
缓存机制:为了提高构建速度,Docker会对每一层进行缓存。如果某一层没有变化,Docker将直接使用缓存的层,而不会重新构建。
生成镜像:所有指令执行完毕后,Docker会生成最终的镜像,并将其存储在本地。你可以通过 docker images命令查看生成的镜像。
3.2核心指令介绍FROM:指定基础镜像,必须为第一个命令# 格式:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
# 示例:
FROM mysql:5.6
# 注:tag或digest是可选的,如果不指定,将使用latest版本的基础镜像LABEL:提供元数据,例如维护者信息、版本等。# 格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 示例:
LABEL maintainer="youremail@example.com"
# 注:使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据。之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。RUN:在镜像内运行命令,例如安装依赖、更新包等。每个RUN指令都会创建一个新的镜像层。# shell格式:等同于在终端操作的shell命令。
RUN <command>
# exec执行格式:
RUN ["executable", "param1", "param2"]
# 示例:
RUN ["executable", "param1", "param2"]
RUN apk update
RUN ["/etc/execfile", "arg1", "arg2"]
# 注:RUN指令创建的中间镜像会被缓存,并在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cacheCMD:指定容器启动时默认执行的命令,可以被docker run命令行参数覆盖。只能有一个CMD指令,如果有多个,只有最后一个生效。# 格式:
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)
# 示例:
CMD echo "This is a test." | wc -l
CMD ["/usr/bin/wc","--help"]
# 注:CMD不同于RUN,CMD用于指定容器启动时要执行的命令,而RUN用于指定镜像构建时要执行的命令。ENTRYPOINT:配置容器启动时的固定命令,通常与CMD配合使用。ENTRYPOINT不会被 docker run 命令行参数覆盖。# 格式:
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件,优先)
ENTRYPOINT command param1 param2 (shell 内部命令)
# 示例:
FROM ubuntu
ENTRYPOINT ["ls", "/usr/local"]
CMD ["/usr/local/tomcat"]
# 注:ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给CMD。Dockerfile中只允许有一个ENTRYPOINT命令,多次指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。通常情况下,ENTRYPOINT与CMD一起使用,ENTRYPOINT写默认命令,当需要参数时使用CMD传参。EXPOSE:声明容器将要监听的端口。# 格式:
EXPOSE <port> [<port>/<protocol>...]
# 示例:
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
# 注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过 -p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口。如果没有暴露端口,后期也可以通过-p 8080:80方式映射端口,但不能通过-P形式映射。ENV:设置环境变量,可以在后续的RUN和CMD中使用。# 格式:
ENV <key> <value> 或 ENV <key>=<value> ...
# 示例:
ENV MY_VAR=my_valueADD:从构建上下文复制文件或URL到镜像中,如果复制的是压缩包,还会自动解压,可访问网络资源。# 格式:
ADD <src>... <dest>
# 示例:ADD ./myfile.txt /app/COPY:与ADD类似,但主要用于复制文件或目录到镜像中,不会解压压缩包,不可以访问网络资源。# 格式:
COPY <src>... <dest>
# 示例:
COPY ./myfile.txt /app/VOLUME:定义一个或多个挂载点,用于存储数据。# 格式:
VOLUME ["/data"]
# 示例:
VOLUME ["/data1", "/data2"]
# 注:一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统。WORKDIR:设置工作目录,对RUN、CMD、ENTRYPOINT、COPY和ADD指令有效。# 格式:
WORKDIR /path/to/workdir
# 示例:
WORKDIR /app
# 注:通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。USER:设置运行容器时的用户。# 格式:
USER <username>
# 示例:
USER myuser
# 注:使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。ARG:定义构建镜像过程中需要使用的变量。这些变量在构建时可以通过--build-arg参数来赋值。# 格式:
ARG <name>[=<default value>]
# 示例:
ARG IMAGE_VERSION=latestONBUILD:定义当镜像被用作其他镜像的基础镜像时的构建指令。# 格式:
ONBUILD <INSTRUCTION>
# 示例:
ONBUILD RUN apt-get update
# 注: ONBUILD后面跟指令,当当前的镜像被用做其它镜像的基础镜像时,该镜像中的触发器将会被触发。STOPSIGNAL:设置发送给容器以退出的信号。# 格式:
STOPSIGNAL <signal>
# 示例:
# 使用默认的SIGTERM信号
STOPSIGNAL SIGTERM
# 使用信号编号15,它等同于SIGTERM
STOPSIGNAL 15
# 注:当容器被请求停止时,Docker会发送SIGTERM信号给容器内的主进程。HEALTHCHECK:定义周期性检查容器健康状态的命令。# 格式:
HEALTHCHECK [OPTIONS] CMD <command>
# 示例:
HEALTHCHECK --interval=30s --timeout=10s --retries=3 CMD curl --fail http://localhost/ || exit 1
# 注:这个例子中,Docker会每隔30秒运行一次curl --fail http://localhost/ 命令来检查容器的健康状态。如果命令失败(即返回非零退出状态),则会在接下来的两次尝试中继续运行该命令,如果三次都失败,则认为容器不健康。SHELL:覆盖Docker默认的shell,用于RUN、CMD和ENTRYPOINT指令。# 格式:
SHELL ["<executable>", "<param1>", "<param2>", ...]
# 示例:
SHELL ["/bin/bash", "-c"]
# 注:这个例子中,后续的RUN、CMD和ENTRYPOINT指令将使用/bin/bash -c来执行命令,而不是默认的 /bin/sh -c
4.1Dockerfile应用CI/CD 流程中的Dockerfile应用以下是一个Python基于flask框架的web项目的Dockerfile示例:# 选择轻量级的Python 3.8镜像
FROM python:3.8-slim
# 标记维护者信息
LABEL maintainer="th-0707"
# 设置工作目录为/app
WORKDIR /app
# 复制依赖文件到容器
COPY requirements.txt /app
# 安装Python依赖
RUN pip install -r requirements.txt
# 复制项目文件到容器
COPY . /app
# 设置Flask应用的文件
ENV FLASK_APP=app.py
# 允许Flask应用在所有网络接口上运行
ENV FLASK_RUN_HOST=0.0.0.0
# 暴露5000端口
EXPOSE 5000
# 容器启动时运行Flask应用
CMD ["flask", "run"]假如当前项目的文件目录如下所示:my_flask_app/
│
├── app.py
├── requirements.txt
└── Dockerfile构建镜像:cd my_flask_app
docker build -t my_flask_app .运行容器:docker run -p 5000:5000 my_flask_app