控制tfenv安装Terraform版本的并发执行以解决权限问题
解决tfenv并行安装Terraform版本的权限错误问题
问题背景
使用Atlantis搭配Terragrunt构建Terraform基础设施时,Terragrunt会为多个工作区并行调用tfenv install安装不同版本的Terraform,引发竞争条件导致权限错误,典型报错如下:
/home/user/.tfenv/lib/tfenv-exec.sh: line 43: /home/user/.tfenv/versions/1.6.2/terraform: Permission denied /home/user/.tfenv/lib/tfenv-exec.sh: line 43: exec: /home/user/.tfenv/versions/1.6.2/terraform: cannot execute: Permission denied
已针对该问题向tfenv提交Issue,尝试编写Shell包装脚本实现tfenv install的串行化执行,但现有脚本未达到预期效果。脚本逻辑设想为:
- Atlantis调用tfenv包装器
- 包装器检查锁状态:无锁则获取锁并执行安装,有锁则等待释放
- 未获取锁的进程检查目标版本是否已安装,已安装则直接退出,否则等待锁释放后尝试安装
现有未生效脚本如下:
#!/bin/bash LOCK_FILE="/tmp/tfenv-wrapper.lock" PROCESS_COUNTER_FILE="/tmp/tfenv-process-counter" MAX_CONCURRENT_PROCESSES=2 # Function to acquire a lock function acquire_lock() { exec 100>"$LOCK_FILE" flock -x 100 } # Function to release the lock function release_lock() { flock -u 100 } # Function to get the current process count function get_process_count() { pgrep -f "tfenv install" | grep -v $$ | wc -l } # Function to update the process count function update_process_count() { get_process_count > "$PROCESS_COUNTER_FILE" } # Set up the EXIT trap to release the lock and remove the lock file on script exit trap 'release_lock' EXIT # Infinite loop to keep the script running while true; do # Acquire the lock acquire_lock echo "Lock acquired." # Read the number of running processes from the counter file num_processes=$(get_process_count) echo "Number of tfenv processes: $num_processes" # If the number of running processes exceeds the limit, wait while [ "$num_processes" -ge "$MAX_CONCURRENT_PROCESSES" ]; do echo "Maximum number of concurrent 'tfenv' processes reached. Waiting for processes to complete." sleep 2 num_processes=$(get_process_count) done # Increment the process counter ((num_processes++)) update_process_count # Release the lock release_lock echo "Released lock." # Wait for any 'tfenv' process launched outside this script to complete while [ "$(get_process_count)" -gt 0 ]; do sleep 2 done # Acquire the lock again before decrementing the counter acquire_lock echo "Lock acquired." # Decrement the process counter ((num_processes--)) update_process_count # Release the lock release_lock echo "Released lock." done
解决方案建议
1. 简化版串行化tfenv包装脚本
原脚本逻辑过于复杂,改用基于文件排他锁的极简方案,确保同一时间仅一个进程执行tfenv install,且优先检查版本是否已安装:
#!/bin/bash # 替换系统tfenv为该脚本,确保串行安装 TARGET_VERSION="$1" LOCK_FILE="/tmp/tfenv-install.lock" # 先检查目标版本是否已安装,避免重复执行 if tfenv list | grep -q "^$TARGET_VERSION$"; then exit 0 fi # 使用flock获取排他锁,确保串行执行安装 # -w 300 表示最多等待5分钟,可根据实际调整 flock -x -w 300 "$LOCK_FILE" bash -c " # 再次检查版本(防止等待期间已被其他进程安装) if ! tfenv list | grep -q "^$TARGET_VERSION$"; then tfenv install \"$TARGET_VERSION\" fi "
使用说明:
- 将该脚本命名为
tfenv,放置在$PATH优先级高于系统tfenv的目录(比如/usr/local/bin) - 确保脚本有可执行权限:
chmod +x /usr/local/bin/tfenv - 脚本会优先检查版本是否存在,已存在则直接退出;未存在则通过flock确保串行安装
2. 预安装所有所需Terraform版本
从根源避免并行安装,在Atlantis启动前批量安装所有工作区需要的Terraform版本:
- 编写脚本遍历所有Terragrunt配置文件,提取
terraform.required_version字段 - 批量执行
tfenv install安装这些版本
示例提取脚本:
#!/bin/bash # 遍历所有terragrunt.hcl文件,提取所需TF版本 find /path/to/your/infra -name "terragrunt.hcl" -exec grep -oP 'terraform.required_version\s*=\s*"\K[^"]+' {} \; | sort -u | while read -r version; do tfenv install "$version" done
使用说明:
- 将该脚本加入Atlantis启动脚本中,确保每次服务启动前都预安装所需版本
- 若配置有更新,重新执行该脚本即可
3. 调整Atlantis并行执行策略
修改Atlantis配置,限制并行处理的工作区数量,或按Terraform版本分组串行执行:
- 在Atlantis的
atlantis.yaml中,通过parallelism字段限制全局并行任务数:parallelism: 1 - 或使用项目分组,将使用相同Terraform版本的工作区归为一组,不同组串行执行:
配合Atlantis的串行执行配置,确保不同版本的分组不会并行触发安装projects: - name: tf-v1.6.x dir: infra/v1.6 workspace: default - name: tf-v1.7.x dir: infra/v1.7 workspace: default
内容的提问来源于stack exchange,提问作者Saifeddine Rajhi
相关产品推荐
相关产品推荐

