From a713c46ed0080b58c3846b082740fe5554bb543a Mon Sep 17 00:00:00 2001 From: emelinda Date: Tue, 11 Aug 2026 03:33:56 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D1=8B=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20=D1=80=D0=B5=D0=BD=D0=B4=D0=B5=D1=80=D0=B0=20=D0=BC=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=84=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20=D0=B8=20?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BA=D0=B0=20=D0=B2=D1=85=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 25 ++++++++++++++ Dockerfile | 77 ++++++++++++++++++++++++++++++++++++++++++ hack/entrypoint.sh | 27 +++++++++++++++ hack/render-contour.sh | 61 +++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 hack/entrypoint.sh create mode 100644 hack/render-contour.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2036709 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +# В контекст сборки нужны только манифесты: clusters/, apps/, infrastructure/ +# и скрипты из hack/. Всё остальное — история, IDE, секреты — только раздувает +# контекст и рискует уехать в образ. +.git +.gitignore +.idea +.claude +CLAUDE.md +.env +.env.* +docs +*.md +aero +inventory.yaml +tmp +.tmp/ +snapshots +out + +# Секреты и ключи не должны попадать в контекст сборки даже случайно. +*.pem +*.key +*.p12 +kubeconfig* +*service-account*.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a320efa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# Полная сборка манифестов IaC-репозитория для одного контура. +# +# docker build --build-arg CONTOUR=aero -t iac:aero . +# docker run --rm iac:aero list # что собралось +# docker run --rm iac:aero cat clusters # слои Flux одним потоком +# docker run --rm -v "$PWD/out:/export" iac:aero export +# +# Контур — имя каталога в clusters/ (aero, brusnika-prod, dsinv, wb, ...). +# Тот же идентификатор служит именем overlay внутри apps/* и infrastructure/*, +# поэтому одного аргумента хватает на весь рендер. +# +# Сборка офлайновая: kustomize build разворачивает оверлеи и патчи, а +# HelmRelease остаётся Custom Resource'ом — чарты тянет Flux уже в кластере. +# Ни helm, ни доступ к OCI-реестру здесь не нужны. Исключение — валидация: +# схемы CRD kubeconform берёт из сети (отключается VALIDATE=0). + +ARG KUSTOMIZE_VERSION=v5.4.3 +ARG KUBECONFORM_VERSION=v0.6.7 + +FROM registry.k8s.io/kustomize/kustomize:${KUSTOMIZE_VERSION} AS kustomize +FROM ghcr.io/yannh/kubeconform:${KUBECONFORM_VERSION} AS kubeconform + + +FROM alpine:3.20 AS builder + +# Контур обязателен по смыслу, но значение по умолчанию оставлено: сборка без +# --build-arg собирает aero, а не падает на пустой переменной. +ARG CONTOUR=aero +ARG VALIDATE=1 +ARG KUBERNETES_VERSION=1.30.0 + +COPY --from=kustomize /app/kustomize /usr/local/bin/kustomize +COPY --from=kubeconform /kubeconform /usr/local/bin/kubeconform + +WORKDIR /src + +# Скрипт копируется отдельным слоем: правка манифестов не инвалидирует его, +# а правка скрипта не тянет за собой пересборку базовых слоёв. +COPY hack/render-contour.sh /usr/local/bin/render-contour +RUN chmod +x /usr/local/bin/render-contour + +COPY . . + +RUN render-contour "${CONTOUR}" /out + +# Валидация отдельным слоем: смена версии Kubernetes не пересобирает рендер. +# --ignore-missing-schemas пропускает ресурсы, чьих схем нет в каталоге +# (часть CRD), но не заглушает ошибки в тех, что нашлись. +RUN if [ "${VALIDATE}" = "1" ]; then \ + kubeconform \ + -kubernetes-version "${KUBERNETES_VERSION}" \ + -strict \ + -ignore-missing-schemas \ + -schema-location default \ + -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \ + -summary \ + /out/*.yaml ; \ + else \ + echo "VALIDATE=0 — проверка схемами пропущена" ; \ + fi + + +FROM alpine:3.20 + +ARG CONTOUR=aero +ENV CONTOUR=${CONTOUR} + +LABEL org.opencontainers.image.title="iac-manifests" \ + org.opencontainers.image.description="Собранные манифесты контура ${CONTOUR}" \ + ru.iac.contour="${CONTOUR}" + +COPY --from=builder /out /manifests +COPY hack/entrypoint.sh /usr/local/bin/entrypoint +RUN chmod +x /usr/local/bin/entrypoint + +ENTRYPOINT ["/usr/local/bin/entrypoint"] +CMD ["list"] diff --git a/hack/entrypoint.sh b/hack/entrypoint.sh new file mode 100644 index 0000000..832f6f0 --- /dev/null +++ b/hack/entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Точка входа образа с собранными манифестами. +# +# list — что собралось +# cat [префикс] — весь контур (или его часть) одним потоком +# export [путь] — скопировать манифесты в примонтированный каталог +# <команда> — выполнить как есть +set -eu + +case "${1:-list}" in + list) + ls -1 /manifests + ;; + cat) + shift + cat /manifests/"${1:-}"*.yaml + ;; + export) + dst="${2:-/export}" + [ -d "$dst" ] || { echo "Каталог ${dst} не примонтирован" >&2; exit 1; } + cp /manifests/*.yaml "$dst"/ + echo "Выгружено в ${dst}" + ;; + *) + exec "$@" + ;; +esac diff --git a/hack/render-contour.sh b/hack/render-contour.sh new file mode 100644 index 0000000..c591537 --- /dev/null +++ b/hack/render-contour.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Рендер всех kustomization'ов, относящихся к одному контуру. +# +# render-contour.sh <контур> <каталог-результата> +# +# Используется из Dockerfile, но работает и локально — нужен только kustomize. +set -eu + +contour="${1:?не задан контур}" +out="${2:?не задан каталог результата}" + +if [ ! -d "clusters/${contour}" ]; then + echo "Неизвестный контур: ${contour}" >&2 + echo "Доступные:" >&2 + ls clusters | sed 's/^/ - /' >&2 + exit 1 +fi + +mkdir -p "$out" + +build() { + dir="$1" + # Имя файла повторяет путь: clusters/aero/apps -> clusters.aero.apps.yaml + name=$(echo "$dir" | tr '/' '.') + echo " build ${dir}" + # --load-restrictor LoadRestrictionsNone: слои ссылаются на apps/ и + # infrastructure/ через ../../.., то есть выходят за корень kustomization. + kustomize build --load-restrictor LoadRestrictionsNone "$dir" > "${out}/${name}.yaml" +} + +# Каталоги контура: сам clusters/ и все его слои (controllers, configs, +# apps, infrastructure — набор у контуров разный, поэтому ищем, а не перечисляем). +# flux-system исключён: gotk-components.yaml генерирует `flux bootstrap`, +# он и так лежит в репозитории готовым. +echo "Контур ${contour}: слои Flux" +find "clusters/${contour}" -name kustomization.yaml -not -path '*/flux-system/*' \ + | sed 's#/kustomization.yaml$##' | sort > /tmp/layers.txt +while read -r dir; do + build "$dir" +done < /tmp/layers.txt + +# Оверлеи приложений и инфраструктуры. Через слои они уже отрендерились, но +# отдельные файлы дают поресурсный diff и ловят оверлей, который в слои пока +# не подключён, — такой ломался бы уже при подключении. +for root in apps infrastructure; do + [ -d "$root" ] || continue + echo "Контур ${contour}: оверлеи ${root}/*/${contour}" + for dir in "$root"/*/"$contour"; do + [ -f "${dir}/kustomization.yaml" ] || continue + build "$dir" + done +done + +# Пустой результат почти наверняка означает опечатку в имени контура — +# лучше упасть на сборке, чем отдать пустой образ. +if [ -z "$(ls -A "$out")" ]; then + echo "Для контура ${contour} не собрано ни одного манифеста" >&2 + exit 1 +fi + +echo "Готово: $(ls -1 "$out" | wc -l) файлов в ${out}"