terraform init упирается в трансграничный Registry, кеш провайдеров и дрейф .terraform.lock.hcl. Ниже — пороги, матрица mirror, ENV/terraform.rc, ворота CI, FAQ. Без входа: главная, блог, Helm OCI на Mac CI.
Три боли: гонка за общим TF_PLUGIN_CACHE_DIR; lock без darwin_arm64; бесконечные ретраи вместо зеркала.
Измеримые критерии и пороги (оценка SLA)
Старт для cross-border + общий APFS; уточняйте по p95 init и 5xx зеркала. На Mac CI важно разделять «скорость сети» и «скорость диска»: десяток параллельных распаковок ZIP в один каталог на APFS может дать хуже p95, чем увеличение TF_REGISTRY_CLIENT_TIMEOUT, поэтому сначала измеряйте, где узкое место, и только потом поднимайте параллель.
| Измерение | Порог «старт» | Интерпретация / действие |
|---|---|---|
p95 времени terraform init |
main < 120 с; ночь до 240 с | Сначала mirror, потом TF_REGISTRY_CLIENT_TIMEOUT. |
TF_REGISTRY_CLIENT_TIMEOUT (сек.) |
локально 10–20; дальше 30–45 | ≤ 4 попытки, backoff 2/4/8 с. |
Параллельные init на одном хосте |
общий диск 2–4; NVMe до 4–6 | Подкаталог на job или очередь. |
| Свободное место до массовых init | > 15 %, лучше > 20 ГиБ | Иначе fail-fast. |
Управление .terraform.lock.hcl |
без скрытых дрейфов хешей | Lock с «золотого» образа + запись команды в PR. |
TF_LOG=INFO кратко — отличить TLS от 404 зеркала; в lock — darwin_arm64 для Mac CI.
Сравнительная таблица и матрица решений
Критерий: доверенный источник ZIP/хешей. Документируйте include/exclude. Если политика безопасности запрещает прямой egress, блок direct должен явно exclude публичный registry, иначе Terraform найдёт легальный обход через неучтённый namespace и снова уйдёт в интернет.
| Режим | Когда уместен | Главный риск | Практический вывод |
|---|---|---|---|
direct к registry.terraform.io |
прототип, мягкий egress | latency, rate limit, DNS | Не только public registry в проде. |
| network_mirror (корпоративный HTTPS) | корп. HTTPS, аудит | индекс/TLS зеркала | Lock на том же образе CI. |
| filesystem_mirror | DMZ, офлайн-пики | рассинхрон каталога | RO для раннера; sync отдельно. |
plugin_cache_dir + уникальный путь job |
повторы одних версий | мультитенант, lock drift | См. plugin_cache_may_break_dependency_lock_file. |
Команды и переменные среды (воспроизводимый чеклист)
${CI_PROJECT_DIR}: версия Terraform → TF_CLI_CONFIG_FILE + TF_PLUGIN_CACHE_DIR → init с backoff → validate + diff lock. Держите бинарник Terraform закреплённым (asdf/tfenv/образ OCI) и не смешивайте patch-версии между job, иначе terraform providers lock даст разные строки на идентичном коде.
export TF_IN_AUTOMATION=1
export TF_INPUT=0
export TF_CLI_CONFIG_FILE="${CI_PROJECT_DIR}/.terraformrc"
export TF_PLUGIN_CACHE_DIR="${CI_PROJECT_DIR}/.tf-plugin-cache-${CI_JOB_ID:-local}"
export TF_REGISTRY_CLIENT_TIMEOUT="${TF_REGISTRY_CLIENT_TIMEOUT:-45}"
# краткий разбор: export TF_LOG=INFO
mkdir -p "${TF_PLUGIN_CACHE_DIR}"
Фрагмент .terraformrc (пути через envsubst):
plugin_cache_dir = "/path/job/.tf-plugin-cache-12345"
provider_installation {
filesystem_mirror {
path = "/usr/local/share/terraform/providers-mirror"
include = ["registry.terraform.io/hashicorp/*"]
}
network_mirror {
url = "https://terraform-mirror.internal.example.com/"
include = ["registry.terraform.io/*/*"]
}
direct {
exclude = ["registry.terraform.io/*/*"]
}
}
terraform providers lock \ -platform=darwin_arm64 \ -platform=linux_amd64 \ -fs-mirror=/usr/local/share/terraform/providers-mirror terraform init -input=false -no-color -upgrade=false terraform validate
run_init() {
local n=1 d=2
while [ "$n" -le 4 ]; do
echo "terraform init attempt ${n}/4"
terraform init -input=false -no-color -upgrade=false && return 0
[ "$n" -eq 4 ] && return 1
sleep "$d"; d=$((d*2)); n=$((n+1))
done
}
run_init
Ворота CI
terraform fmt -check -recursiveиterraform validateна защищённых ветках.terraform init -lockfile=readonlyв обычных job; обновление lock — отдельный контролируемый pipeline.git diff --exit-code -- .terraform.lock.hcl(или эквивалент), чтобы PR отражал намерение по lockfile.- Префлайт диска перед init.
FAQ
Q: трафик всё ещё в public registry? A: проверьте include/exclude, community namespace и фактический URL в TF_LOG=INFO; часто остаётся «запасной» direct без нужного exclude.
Q: два корня модулей? A: у каждого свой .terraform.lock.hcl; в матрице CI вызывайте init -backend=false в подкаталогах или отдельными job.
Q: чистить TF_PLUGIN_CACHE_DIR каждый раз? A: обычно нет; чистка при повреждении кеша, смене арендатора узла или жёсткой политике ИБ.
Q: очередь на общем Mac? A: снизьте параллель init, поднимите mirror ближе к площадке, ограничьте число ретраев — иначе один «плохой» провайдер блокирует соседние job.