OpenShift集群中Tekton执行npm ci报EMFILE文件打开过多错误排查
问题描述
我在OpenShift集群中使用Tekton流水线构建Angular应用,流水线包含两个ClusterTask:
git-clone:拉取源码buildah:构建并推送镜像
在执行Dockerfile中的RUN npm ci步骤时,出现如下错误:
npm ERR! code EMFILE npm ERR! syscall open npm ERR! path /root/.npm/_cacache/index-v5/d8/c0/7503a3169361b6a2c8813bf0eddb92eed6417987f616accf18698f2b71f4 npm ERR! errno -24 npm ERR! EMFILE: too many open files, open '/root/.npm/_cacache/index-v5/d8/c0/7503a3169361b6a2c8813bf0eddb92eed6417987f616accf18698f2b71f4' npm ERR! A complete log of this run can be found in: /root/.npm/_logs/2024-03-27T13_42_33_018Z-debug-0.log subprocess exited with status 232 subprocess exited with status 232 Error: building at STEP "RUN npm ci": exit status 232
推测是npm打开文件数超出容器默认ulimit(1024),但相同操作的OpenShift Build Config可正常运行,怀疑差异在于Build Config使用特权上下文,想明确问题根源以及Tekton任务使用特权上下文的安全性。
问题根源
Build Config与Tekton的权限差异
OpenShift Build Config默认使用的构建容器(如Source-to-Image或Docker构建器)以特权模式运行,特权模式下容器会继承宿主机的文件描述符限制(通常为65535或更高),因此不会触发EMFILE错误。而Tekton的buildahClusterTask默认以非特权模式运行,容器的默认nofile(最大打开文件数)ulimit为1024,npm ci安装Angular依赖时会同时打开大量缓存文件、依赖包文件,超出该限制就会报错。特权上下文的安全风险
给Tekton任务启用特权模式会授予容器几乎与宿主机等同的权限,包括访问宿主机设备、修改内核参数、挂载宿主机文件系统等。这意味着如果构建过程中引入恶意代码(如恶意依赖、被篡改的源码),攻击者可通过特权容器突破到宿主机,进而威胁整个集群的安全。除非有绝对必要,否则不建议使用特权模式。
非特权模式下的解决方案
1. 调整Tekton Task的ulimit限制
修改buildah ClusterTask的容器定义,添加securityContext配置提升nofile的软/硬限制:
apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: buildah # 或对应ClusterTask的名称 spec: steps: - name: build securityContext: ulimits: - name: nofile soft: 65535 hard: 65535 # 其他步骤配置...
该配置会将容器的最大打开文件数提升到65535,满足npm ci的文件操作需求。
2. 限制npm并发数
通过npm参数或配置文件减少并发操作,降低同时打开的文件数量:
- 在Dockerfile中修改命令:
RUN npm ci --maxsockets 10 - 或在项目根目录添加
.npmrc文件:maxsockets=10
3. 启用npm依赖缓存
使用Tekton Workspace缓存npm的node_modules和缓存目录,避免每次构建重复下载依赖,减少文件操作次数:
在PipelineRun中定义缓存Workspace,并在buildah Task中挂载:
apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: generateName: angular-build- spec: workspaces: - name: npm-cache persistentVolumeClaim: claimName: npm-cache-pvc pipelineSpec: workspaces: - name: npm-cache tasks: - name: git-clone taskRef: name: git-clone workspaces: - name: output workspace: source-code - name: buildah taskRef: name: buildah runAfter: [git-clone] workspaces: - name: source workspace: source-code - name: npm-cache workspace: npm-cache params: - name: BUILD_EXTRA_ARGS value: "--build-arg NPM_CONFIG_CACHE=/workspace/npm-cache"
同时在Dockerfile中指定缓存路径:
ARG NPM_CONFIG_CACHE=/root/.npm RUN npm ci
内容的提问来源于stack exchange,提问作者Otrebor

