Для кого: общие удалённые Mac (Apple Silicon), где 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.

Итог

Свяжите таймаут, mirror, кеш, lock, ограниченные ретраи. Нужен выделенный Apple Silicon — цены, помощь, покупка; ещё материалы в блоге (всё без входа).

Зафиксируйте версию «золотого» образа в README/CI; любое изменение .terraform.lock.hcl — только с записью terraform providers lock.