【容器】设备上没有剩余空间的错误排查处理
推荐超级课程:
设备上没有剩余空间
是一个相对知名的错误,可以发生在运行基于Linux的程序的环境中。如果存储位置已满,就会发生这种情况,错误表明该位置不再有空间来写入/存储更多数据。这本身并不是Azure容器应用特有的,但由于两个核心原因,这里可能会出现这个问题:
镜像大小
容器向容器文件系统写入大量文件(例如缓存、大文件、大量文件等)。重要的是写入本地文件系统,而不是挂载的Azure文件卷。使用Azure文件持久化的卷将数据存储在文件共享中。写入本地容器文件系统的数据存储在主机上存储层的共享位置。这通常被称为容器的“薄可写层”。
- 另一方面,如果使用临时卷
,您也可能看到类似的
设备上没有剩余空间
问题,不同之处在于,由于达到存储限制,Pod将被驱逐
。正如上述链接所示,卷大小取决于分配给容器的cpu
。
- 另一方面,如果使用临时卷
,您也可能看到类似的
上述情况可以发生在作业和容器应用上
可用的配置文件类型可以在这里
找到。以下是不同配置文件的磁盘空间示例,您可以使用df -h
查看
(消费型)
(d4)
随着SKU大小的增加,磁盘空间也会增加。
在存储方面,overlay
在满时会导致这个问题。注意,完全预期 overlay
上已经有一些使用量,无论镜像大小如何。由于这个位置是主机上的挂载点,因此无法直接访问。
发现这些错误
您可以在日志流、Azure Monitor 或 Log Analytics 中找到这些错误。有关容器应用的日志选项,请查看 Azure Container Apps 中的日志存储和监控选项
在 Azure Monitor 中,它将在 ContainerAppSystemLogs
表中。对于 Log Analytics,它将在 ContainerAppSystemLogs_CL
表中。
这可能在镜像拉取过程中直接显示 - 另一方面可能是在容器创建时,如果在容器创建时磁盘空间已满,将显示此消息。
无论哪种情况,它看起来都会像下面这样:
OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting \"/some/path" to rootfs at \"/somepath\": mkdir /mnt/to/somepath: no space left on device: unknown"}
或者简单地说:
no space left on device: unknown
镜像大小
最大镜像大小在 这里 中指出。
最大镜像大小:
消费型工作负载配置文件支持每个应用或作业副本的容器镜像总大小达到 8GB。
专用工作负载配置文件支持更大的容器镜像。 因为专用工作负载配置文件可以运行多个应用或作业,所以多个容器镜像共享可用的磁盘空间。实际支持的镜像大小根据其他应用和作业消耗的资源而变化。
上述原因在于消费型和工负载配置文件之间的托管机制差异。
由于 工作负载配置文件 可以被视为更典型的“节点”基于方法,其中使用配置文件的所有Pods/副本将在这些节点上调度,这就是上面提到的 多个容器镜像共享可用磁盘空间 的概念来源。
最佳实践(总的来说):
尽可能使用更小的镜像。这可能意味着使用
alpine
或slim
基于的镜像分发版本,而不是完整的分发版如:ubuntu
、:debian
等。检查您的基础镜像是否支持更小的变体标签。- 一个需要注意的是,像 Alpine 这样的较小变体将使用
apk
包管理器(因此,如果从另一个分发版切换,您可能需要更新您的Dockerfile
和其他.sh
脚本)- 最重要的是 - 某些工具可能默认不可用/未安装,因为其理念是保持小型化。- 此外,Alpine 使用musl
作为 C 标准库,而不是其他发行版中常见的glibc
。这可能不会影响大多数应用程序,但应该牢记。特别是如果应用程序依赖于共享库或底层 C 绑定。
- 一个需要注意的是,像 Alpine 这样的较小变体将使用
使用多阶段镜像。更多信息请参见 多阶段构建 。多阶段构建可以通过仅从之前的构建中复制所需内容到当前构建的上下文中,并丢弃其余内容来节省大量空间,而不是创建一个大的构建。例如:
FROM maven:3.9.0-eclipse-temurin as build WORKDIR /usr/src/app COPY . /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package -DskipTests FROM eclipse-temurin:17.0.14_7-jre WORKDIR /usr/src/app COPY --from=build /usr/src/app/target/probes-0.0.1-SNAPSHOT.jar probes-0.0.1-SNAPSHOT.jar COPY --from=build /usr/src/app/init_container.sh init_container.sh COPY --from=build /usr/src/app/certs /usr/src/app/certs
这个构建使用 Maven 构建上述 Java 应用程序以生成一个
.jar
文件,我们只将这个文件和其他一些文件复制到当前的运行时构建中。这样可以节省空间,因为不需要在镜像中包含 Maven 及其所有其他依赖项作为依赖本身。
使用
.dockerignore
(或您容器管理工具的等效文件)。可以将其视为类似于.gitignore
,但用于放入镜像的文件。在某些情况下,这可以减少许多文件并节省整体镜像大小。参见 .dockerignore 文件 。如果使用 Podman,请使用 .containerignore考虑仅使用运行时环境而不是完整的开发工具包(SDKs)。如果不需要完整的 SDKs(或 JDKs)- 考虑使用仅运行时环境等效物。对于 Java,这通常是包含
jre
标签的镜像,对于 .NET,这是名称中仅包含aspnet:some.version
的镜像 - 请参见这里 了解相关解释如果遇到这个问题,并且您想检查镜像大小,请查看未压缩的镜像大小,而不是压缩大小。当拉取层时,存储在注册表中的压缩镜像的层会被下载并解压缩,因此这个大小才是重要的。要有效检查镜像大小,请将其拉取到您的本地机器(如果尚不存在)并运行以下命令:
docker inspect -f "{{ .Size }}" someimage:sometag
- 这会输出未压缩的大小(以字节为单位)docker inspect -f "{{ .Size }}" someimage:sometag | numfmt --to=si
- 这会以人类可读的格式输出未压缩的大小-docker image ls someimage:sometag
以下是使用此命令在本地镜像上的一些示例:
$ docker inspect -f "{{ .Size }}" pythonfastapi:large
12155661241
$ docker image ls pythonfastapi:large
REPOSITORY TAG IMAGE ID CREATED SIZE
pythonfastapi large ab7065c19777 6天前 12.2GB
$ docker inspect -f "{{ .Size }}" pythonfastapi:large | numfmt --to=si
13G
有时,您可能不得不依赖第三方镜像,其中您无法控制镜像大小。这也可能适用于主要专注于AI/ML的某些镜像,它们天生具有大型库。如果没有更瘦小/更小的标签可以利用,那么考虑使用工作负载配置文件并使用更大的SKU大小。
运行时
这指的是概述部分中关于容器在运行时创建文件的第二点。
如果应用程序生成大量缓存(并且持续添加到其中),或者本质上任何其他操作,随着时间推移,在写入本地文件系统时创建大量文件,那么如果文件数量在一段时间内显著消耗存储空间,它们就可能在运行时遇到这个问题。
另一个场景是使用容器应用作业和可能将其管道写入大量数据的CI/CD运行器时,可能会发生设备上没有剩余空间
的情况。
这些问题可能更难调查,因为如果容器重新启动,写入磁盘的内容现在将被丢弃。然而,在遇到这个问题之前(并且知道这是应用程序的问题),您可以进入控制台 ,它连接到容器中的shell,并可以使用以下命令:
df -h
find
- 您可以使用类似find / -type f -exec du -Sh {} + | sort -rh | head -n 5
的命令 - 其中/
是您想要列出顶部n
目录大小的目录du
- 例如du -a /tmp | sort -n -r | head -n 5
- 其中/tmp
是您想要列出顶部n
目录大小的目录ls -lrtah
- 其中h
显示人类可读的文件大小格式
如果您想列出超过前5的目录,将5替换为n
- 例如,-n 100
。
或者,混合使用其他命令来查找当前磁盘空间使用情况以及哪个目录可能消耗最多的存储空间。这可以以更多适应您用例/需求的不同方式完成。
注意:上述方法不适用于作业,因为无法访问控制台
如果不能这样做,那么在更本地或受控的环境中测试这一点(例如,在Linux VM上,但不在容器内,以便文件持久化到VM磁盘)并进行应用程序调查将需要了解写入磁盘的内容是什么导致这个问题。除了从您的容器中识别出占用磁盘空间的文件/目录本身外,以下操作也可以进行:
使用工作负载配置文件,而不是按需消费,并在需要时使用更高规格的SKU。
如果这些文件本身与缓存无关,并且不打算被丢弃,那么考虑通过存储卷使用AzureFiles来存储这些数据。注意,在这种情况下,不建议使用AzureFiles来存储临时文件。
如果这些文件是与缓存相关的——而且是临时文件——考虑实现某种清理机制,在
x
时间间隔或目录文件大小达到一定程度时进行清理。- 注意,这将意味着首先已知生成如此大量文件的核心原因/位置。
所有上述内容也适用于使用临时卷
,尽管错误可能不是设备上没有剩余空间
,而可能是如下所示的内容,这些内容也可以在系统日志中找到:
容器somecontainer超出了其本地临时存储限制"[some_size]"。
Pod的本地临时存储使用量超过了容器的总限制[some_size]。