Terraform Plan间歇性报Context Canceled错误解决方案咨询
问题背景
在AWS CodeBuild环境执行Terraform操作时,terraform init可稳定运行无异常,但terraform plan会间歇性抛出如下错误:
Error: rpc error: code = Canceled desc = context canceled
当前环境固定版本约束:
- Terraform版本:0.12.31
- AWS Provider版本:~> 3.55.0
版本调整原因:本次操作目标是将Python Lambda运行时从3.6升级至3.9,因此将Terraform从0.12.21升级到0.12.31,AWS Provider从>2系列升级到>3.55.0(该版本是首个支持Python 3.9 Lambda运行时的AWS Provider正式版本)。已知同类问题可通过升级更高版本Provider解决,但当前环境无法继续升级Terraform和AWS Provider版本,否则会破坏现有CI/CD流水线其他环节的兼容性,需要可落地的排查、解决方向。
排查与解决方向
这个错误本质是Terraform主进程和AWS Provider插件进程之间的RPC通信被异常中断,不用急着改版本,按以下优先级排查即可:
- 先排查CodeBuild环境的资源与超时限制
这是这类间歇性错误最高发的诱因。3.x系列AWS Provider比2.x系列内存占用高30%左右,0.12版Terraform本身的内存开销也比0.12.21版本略高,如果CodeBuild用的是默认最小规格(3G内存)实例,存量资源较多时很容易触发OOM杀掉Provider插件进程,直接表现就是RPC连接断开;另外如果plan执行时长接近CodeBuild默认的1小时构建超时阈值,也会被系统强制中断抛出该错误。
排查方式很简单,在buildspec的plan执行步骤后加两行命令:dmesg -T | grep -i oom打印内核OOM日志,echo "Plan exit code: $?"打印命令退出码,如果看到OOM记录直接把CodeBuild计算规格升一档到7G内存,如果是超时直接拉长构建超时时间即可,完全不需要改动Terraform和Provider版本。 - 降低Terraform默认并行度
0.12版本Terraform默认会按10*CPU核数的并行度并发拉取资源状态,3.55版本AWS Provider在高并发调用Lambda、EC2类API时偶发会出现请求挂死,触发主进程主动取消上下文。执行plan时手动指定低并行度测试即可:terraform plan -parallelism=2,如果降到2之后不再复现,再逐步往上调找到当前环境能稳定运行的最高并行度,这个参数只影响API调用并发,不会改动资源定义、也不会破坏流水线兼容性。 - 关闭Provider非必要的元数据检查
3.55版本AWS Provider默认会尝试拉取EC2元数据服务获取凭据和区域信息,CodeBuild环境的凭据是通过环境变量注入的,如果构建环境没有放通元数据服务访问权限,这部分请求会超时拖垮RPC连接。在aws provider块里增加如下配置即可:
注意不要随意开启provider "aws" { region = "你的目标部署区域" skip_metadata_api_check = true skip_credentials_validation = false skip_requesting_account_id = false # 单区域部署不需要跨可用区校验时可以打开下面配置进一步减少API调用 # skip_get_available_zones = true }skip_credentials_validation,避免权限配置错误漏检。 - 配置插件缓存避免二进制损坏
CodeBuild每次构建默认启动全新环境,如果从Terraform Registry下载Provider插件时遇到网络波动,导致二进制文件不完整,也会出现偶发的RPC调用异常。可以在buildspec里配置TF_PLUGIN_CACHE_DIR环境变量指向缓存目录,同时把该目录加入CodeBuild的缓存配置,第一次构建下载完合法的插件包后,后续构建直接复用本地缓存,避免重复下载带来的文件损坏问题。 - 增加API重试配置、错峰执行构建
3.x版本AWS Provider的API调用频率比2.x版本高很多,如果同账号下同时运行多个Terraform构建任务,很容易触发AWS侧的API限流,请求挂死后就会抛出context canceled错误。可以在provider块里增加重试配置:
同时把同账号下的多个Terraform构建任务错峰调度,避免集中打满API配额。provider "aws" { region = "你的目标部署区域" max_retries = 10 } - 拆分大体积State文件
如果单个State文件管理的资源量超过500个,0.12版本Terraform的单进程内存占用很容易触顶,同时Provider插件的长连接也容易因为长时间无数据传输被CodeBuild的网络层主动断开。可以按业务域把大State拆分为多个独立的State或Module,单次plan处理的资源量控制在200以内,不仅能解决这个偶发报错,还能大幅缩短plan执行时长。
内容的提问来源于stack exchange,提问作者csgarun
相关产品推荐
相关产品推荐

