++ offline provider mirror for contour runner

This commit is contained in:
Kochetkov S 2026-08-27 11:30:08 +03:00
parent 57ec941046
commit 69efede7b6
5 changed files with 114 additions and 0 deletions

View File

@ -0,0 +1,7 @@
FROM cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.11
COPY provider-mirror /opt/terraform/provider-mirror
COPY terraformrc /root/.terraformrc
ENV TF_CLI_CONFIG_FILE=/root/.terraformrc
ENV CHECKPOINT_DISABLE=1

View File

@ -0,0 +1,65 @@
# Offline terraform/terragrunt runner
Образ `terraform/terragrunt:v9.11` из `cr.yandex/crp3ccidau046kdj8g9q` тянет провайдеры terraform (`kubernetes`, `vault`, `mongey/kafka`, `cyrilgdn/postgresql`, `cyrilgdn/rabbitmq`, `aminueza/minio`, `random`, `tls`) с `registry.terraform.io` при каждом `terraform init`. В закрытом контуре без доступа к этому хосту `init` виснет или падает.
Этот образ решает именно это: провайдеры зашиты внутрь как файловое зеркало, `terraform init` их больше никуда не тянет. Доступ к `cr.yandex` при этом всё ещё нужен — сам образ раннер тянет оттуда.
## Что внутри
Поверх `cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.11` добавлено:
- `/opt/terraform/provider-mirror` — файловое зеркало (`terraform providers mirror`), собранное под `linux_amd64` по версиям из `required_providers` в `live/terragrunt.hcl`
- `/root/.terraformrc` с `provider_installation { filesystem_mirror {...} }`, указывающим terraform брать провайдеры оттуда, а не с `registry.terraform.io`
- `TF_CLI_CONFIG_FILE=/root/.terraformrc`
- `CHECKPOINT_DISABLE=1` — отключает фоновую проверку новой версии terraform на `checkpoint.hashicorp.com` (не блокирует прогон при недоступности, но незачем тратить время на таймаут)
`sops` уже был в базовом образе `v9.11` — отдельно решать не пришлось, шаг "Decrypt secret values" в workflow ничего с интернета не тянет ни в базовом, ни в этом образе.
## Проверено, не только собрано
`terraform init` реально прогонялся в контейнере с `docker run --network none` — все 8 провайдеров разрешились из локального зеркала, `Terraform has been successfully initialized!`. Не теория, а факт.
## Собрать и залить заново
Нужно, когда меняется набор/версии провайдеров в `live/terragrunt.hcl`, или обновляется базовый образ `terraform/terragrunt`.
```bash
cd docker/offline-runner
PUSH=true ./build.sh
```
`build.sh` сам:
1. качает `terraform/terragrunt:v9.11` под `linux/amd64` (с маком без этого флага уедет arm64, и джоба под упадёт `exec format error`)
2. пересобирает `provider-mirror/` через `terraform providers mirror -platform=linux_amd64` из `versions.tf` в этой же папке
3. собирает образ `cr.yandex/crp3ccidau046kdj8g9q/contour/terraform-terragrunt-offline:v9.11`
4. заливает его, если `PUSH=true`
Локальную папку `provider-mirror/` (около 75 MB бинарников) в git не кладём — она пересобирается заново каждый билд, коммитить туда нечего, кроме `Dockerfile`/`terraformrc`/`versions.tf`/`build.sh`.
**`versions.tf` в этой папке должен быть зеркалом `required_providers` из `live/terragrunt.hcl`.** Если там появится новый провайдер или сменится версия — сначала правится `live/terragrunt.hcl`, потом та же запись копируется сюда и гонится пересборка. Рассинхрон тихий: `terraform init` в офлайн-контуре просто не найдёт новый провайдер в зеркале и упадёт с ошибкой резолва версии, а не с понятным "provider not in mirror".
Текущий образ уже собран и залит: `cr.yandex/crp3ccidau046kdj8g9q/contour/terraform-terragrunt-offline:v9.11`, `linux/amd64`, digest `sha256:9beff996644afad1429f5568f1549501fcb1efe58df8dcd5882ea9dc5a8ffed4`.
## Как подключить в конкретном контуре
Ничего в `.gitea/workflows/terraform.yml` менять не нужно — там уже есть переопределение:
```yaml
image: ${{ vars.RUNNER_IMAGE || 'cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.11' }}
```
В Gitea нужного контура (Settings → Actions → Variables) добавить репозиторную переменную:
```
RUNNER_IMAGE = cr.yandex/crp3ccidau046kdj8g9q/contour/terraform-terragrunt-offline:v9.11
```
`vars.*` в Gitea Actions — переменные конкретного репозитория в конкретном gitea-инстансе, они не синкаются вместе с содержимым ветки. Установка `RUNNER_IMAGE` в одном контуре не задевает остальные — у них как был, так и останется дефолтный образ, пока кто-то явно не пропишет то же самое там.
После этого прогоняется обычный `validate`/`plan`/`apply` — разницы в шагах нет, разница только в том, откуда `terraform init` берёт провайдеры.
## Что офлайн-образ не решает
- Доступ к `cr.yandex` всё равно нужен — и на пул этого образа раннером, и на пул образов приложений (`kafka-contour`, `camunda-contour`, `postgresql-contour` и так далее — их тянет flux/kubelet, не terraform, к этому образу отношения не имеет).
- Шаг "Configure module registry auth" в workflow настраивает `git config` на `gitlab.sarex.io` под внешние `terraform { source }` — в самом контуре все `source` локальные (`../../modules//...`), так что это безвредная команда без сети. Если когда-нибудь появится стек с внешним source на gitlab, для него нужен будет реальный доступ к `gitlab.sarex.io`, этот образ его не даст.
- S3-бэкенд стейта, providers kubernetes/vault/postgresql/kafka/rabbitmq/minio в рантайме ходят на локальные адреса контура (MinIO, Vault, кластер, VM) — они и без этого образа не лезли в интернет, тут ничего не поменялось.

19
docker/offline-runner/build.sh Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
BASE_IMAGE="cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.11"
TARGET_IMAGE="${TARGET_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/contour/terraform-terragrunt-offline:v9.11}"
rm -rf provider-mirror
terraform providers mirror -platform=linux_amd64 provider-mirror
docker pull --platform linux/amd64 "$BASE_IMAGE"
docker build --platform linux/amd64 -t "$TARGET_IMAGE" .
if [[ "${PUSH:-false}" == "true" ]]; then
docker push "$TARGET_IMAGE"
fi
rm -rf provider-mirror

View File

@ -0,0 +1,9 @@
provider_installation {
filesystem_mirror {
path = "/opt/terraform/provider-mirror"
include = ["registry.terraform.io/*/*"]
}
direct {
exclude = ["registry.terraform.io/*/*"]
}
}

View File

@ -0,0 +1,14 @@
terraform {
required_version = ">= 1.5.0"
required_providers {
kubernetes = { source = "hashicorp/kubernetes", version = "~> 2.23" }
vault = { source = "hashicorp/vault", version = "~> 4.2" }
kafka = { source = "Mongey/kafka", version = "0.10.4" }
postgresql = { source = "cyrilgdn/postgresql", version = "~> 1.21" }
rabbitmq = { source = "cyrilgdn/rabbitmq", version = "~> 1.8" }
minio = { source = "aminueza/minio", version = "~> 2.0" }
random = { source = "hashicorp/random", version = "~> 3.1" }
tls = { source = "hashicorp/tls", version = "~> 4.0" }
}
}