Skip to content

08.Docker Storage

Docker 镜像层

在使用 Dockerfile 构建镜像时,每一个 Dockerfile 指令都会创建一个新的 Docker 镜像层,每一层都是基于上一层的构建,只会包含和上一层的新的更改,因此大小不会迅速增加。

如以下两个 Dockerfile 文件:

dockerfile

dockerfile
FROM ubuntu

# 更换 Ubuntu 软件源为阿里云源
RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list && \
    sed -i 's|http://security.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list && \
    apt-get update && \
    apt-get -y install python3 python3-pip

# 配置 pip 使用阿里云镜像
RUN mkdir -p ~/.pip && \
    echo "[global]\nindex-url = https://mirrors.aliyun.com/pypi/simple/\n[install]\ntrusted-host = mirrors.aliyun.com" > ~/.pip/pip.conf

# 安装 Flask
RUN pip install flask --break-system-packages

# 复制源代码到容器中
COPY . /opt/source-code/

# 设置工作目录
WORKDIR /opt/source-code

# 使用 Flask 运行应用
ENTRYPOINT ["flask", "run"]

dockerfile2

dockerfile
FROM ubuntu

# 更换 Ubuntu 软件源为阿里云源
RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list && \
    sed -i 's|http://security.ubuntu.com/ubuntu/|http://mirrors.aliyun.com/ubuntu/|g' /etc/apt/sources.list && \
    apt-get update && \
    apt-get -y install python3 python3-pip

# 配置 pip 使用阿里云镜像
RUN mkdir -p ~/.pip && \
    echo "[global]\nindex-url = https://mirrors.aliyun.com/pypi/simple/\n[install]\ntrusted-host = mirrors.aliyun.com" > ~/.pip/pip.conf

# 安装 Flask
RUN pip install flask --break-system-packages

# 复制源代码到容器中
COPY file.txt /opt/source-code/

# 设置工作目录
WORKDIR /opt/source-code

# 使用 Flask 运行应用
ENTRYPOINT ["flask", "run"]

观察文件,两个 Dockerfile 文件中只有 COPY 指令不同,第一个为 COPY . /opt/source-code/ ,第二个为 COPY file.txt /opt/source-code/

可以在当前目录创建文件,以便保持复制的文件确实不同

先构建 dockerfile 的镜像:

cmd
docker build -f .\dockerfile -t flask-app .

使用 docker images 查看当前镜像的大小,为 549MB :

cmd
PS X:> docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
flask-app    latest    12dd24c0bcd1   2 minutes ago   549MB

然后构建 dockerfile2 的镜像:

cmd
docker build -f .\dockerfile2 -t flask-app2 .

再次查看镜像大小:

cmd
PS X:> docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
flask-app2   latest    447a485a4d4f   6 seconds ago   549MB
flask-app    latest    12dd24c0bcd1   3 minutes ago   549MB

发现两个镜像大小相同,但实际上两个镜像一共占用宿主机约为 549MB 大小,而不是两个镜像大小之和 549 + 549 = 1098MB 。原因是两个 Dockerfile 文件除了 COPY 指令前的所有指令全部相同,因此之前的每个所有指令都会创建一个新层,而这些层会被创建第二个镜像时复用,而不是创建新的层,因此他们共享同一个镜像文件(共享的是底层的文件)。

只有当所有共享的镜像文件都被删除时,原本的层才会被删除。当删除第一个构建的镜像 flask-app 时,镜像 flask-app2 仍可使用。

容器层

容器层是可读可写的,镜像层是只读的:

Snipaste_2025-01-27_21-49-38

所以镜像中的文件不可被修改。

使用镜像创建容器后,容器内的文件会拷贝一份到容器内,可在容器内修改这些文件,如上图中的 app.py 文件;可在容器内创建新文件,如上图中的 temp.txt 文件。

卷映射

容器内创建的数据不会被持久化保存,若想要持久化保存,则将数据保存到宿主机上,建立容器内和宿主机目录(或)之间的映射,使得容器内产生的文件到容器内的目标目录后,相当于保存到了宿主机上的指定目录(卷)。

在 Linux 系统上,卷的默认保存位置在 /var/lib/docker/volumes 中,但也可以显式指定映射的目标目录。使用 -v 选项指定映射:

bash
docker run -v data_volumes:/var/lib/mysql mysql

image-20250127221311250

这里给出了宿主机上的相对路径,使用了 volume 模式。

相当于将宿主机上 /var/lib/docker/volumes/data_volumes 目录和容器内 /var/lib/mysql 目录建立映射,此后在容器内产生到 /var/lib/mysql 目录下的文件都将被保存到宿主机的 /var/lib/docker/volumes/data_volumes 内,实现持久化。

data_volumes 是 Docker 内部管理的卷名称,而不是宿主机上的目录,默认存储在 /var/lib/docker/volumes/ 目录下。

volumes 目录下会存在一个名为 data_volumes 的文件夹,下面会有一个 _data 的文件夹,产生的文件将存放此文件夹内, data_volumes 理解为一个卷,如果没有该卷,Docker 会自动创建它。

也可以显式指定宿主机上的其他目录,使用绝对路径,此时使用的是 bind 模式:

bash
docker run -v /dara/mysql:/var/lb/mysql mysql

如果使用 bind 模式,则映射到宿主机上的为目录而不是卷。

IMPORTANT

注意, -v 选项是一个过时的选项,现在要建立卷映射,一般使用 –mount 选项。

--mount 选项:

使用 --mount 选项建立映射的指令格式如下:

bash
docker run --mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql
  • 指定使用 bind 模式,后面给出绝对路径( mount 强制要求给出绑定的模式, bind 或者 volume
  • source 给出宿主机绝对路径
  • target 给出容器内路径

NOTE

--mount 的语法更严格,比如 source 必须显式为绝对路径。

  • 使用 -v 选项时,Docker 会自动根据路径判断是创建一个 卷(volume) 还是 绑定挂载(bind mount)
  • 卷(Volume) 存储在 Docker 管理的 /var/lib/docker/volumes/ 目录下,并且在卷内部有一个 _data 子目录来存储实际数据。
  • 绑定挂载(Bind Mount) 使用宿主机上的路径作为挂载点,数据直接存储在宿主机的目录中。
  • 推荐使用 --mount 选项,它提供了更清晰和严格的语法。