fix(build): default Kaniko and init images to Harbor via Helm values
Build and Deploy Platform / build-and-deploy (push) Successful in 14m57s

User-app builds no longer pull gcr.io/docker.io directly when build.images
is configured in values.yaml, fixing ImagePullBackOff on clusters without
upstream registry access.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-07-03 14:20:36 +03:30
parent 8163665c86
commit d3bbc0c0a0
10 changed files with 82 additions and 24 deletions
+4 -3
View File
@@ -16,7 +16,7 @@
| دسته | تعداد | ✅ | ⚠️ | 🔜 |
|------|-------|----|----|-----|
| بلاکرهای پروداکشن | 8 | 8 | 0 | 0 |
| بیلد و Kaniko | 10 | 8 | 1 | 1 |
| بیلد و Kaniko | 10 | 9 | 0 | 1 |
| دیپلوی و پیش‌نمایش | 6 | 6 | 0 | 0 |
| دیتابیس / سرویس اختیاری | 9 | 8 | 0 | 1 |
| Migration / اسکیما | 4 | 4 | 0 | 0 |
@@ -57,7 +57,7 @@
| Zip slip در unzip | ✅ | اعتبارسنجی مسیر قبل و بعد از extract |
| state بیلد در حافظه | ✅ | Redis session + startup recovery |
| دیپلوی هم‌زمان بدون قفل | ✅ | in-flight guard در `triggerDeployment` |
| Base image بدون آینه | ⚠️ | `BASE_IMAGE_REGISTRY` اضافه شده؛ باید در env پروداکشن ست شود |
| Base image بدون آینه | | `build.images` در Helm values + پیش‌فرض Harbor در configuration |
| zip bomb (۱۰GiB) | 🔜 | سقف آپلود موجود؛ محدودیت تعداد entry در archive پیشنهاد می‌شود |
---
@@ -157,7 +157,8 @@
|--------|---------|-------|
| `CLUSTER_DEPLOY_FALLBACK_ENABLED` | `false` | fallback بین کلاستر |
| `CLUSTER_DEPLOY_FALLBACK_ATTEMPTS` | `3` | فقط وقتی fallback فعال |
| `BASE_IMAGE_REGISTRY` | — | آینه base imageهای بیلد |
| `BASE_IMAGE_REGISTRY` | `registry.abrban.com/proxy-dockerhub/library` | آینه base imageهای بیلد (یا از `build` در Helm values) |
| `KANIKO_IMAGE` | `registry.abrban.com/proxy-gcr/...` | Kaniko executor (یا `build.images.kaniko` در values) |
| `BUILD_PVC_STORAGE_CLASS` | — | StorageClass برای PVC بیلد |
| `BILLING_WALLET_HMAC_SECRET` | — | اجباری در production |
| `PAYMENT_GATEWAY_*` | — | اجباری برای charge واقعی |
+4 -1
View File
@@ -287,7 +287,10 @@ curl -s -o /dev/null -w '%{http_code}\n' https://<frontend.host>
| `MIZBANSMS_USERNAME` / `MIZBANSMS_PASSWORD` / `MIZBANSMS_FROM` | OTP SMS credentials (required or OTP send 503s) | — |
| `REGISTRY_URL` / `REGISTRY_PULL_URL` | In-cluster registry (push / pull) | `registry.cloudhost-builds.svc.cluster.local:5000` |
| `BUILD_NAMESPACE` / `BUILD_SERVICE_ACCOUNT` | Build Jobs namespace + SA | `cloudhost-builds` / `kaniko-builder` |
| `KANIKO_IMAGE` | Kaniko executor image | `gcr.io/kaniko-project/executor:v1.23.2` |
| `KANIKO_IMAGE` | Kaniko executor image | `registry.abrban.com/proxy-gcr/kaniko-project/executor:v1.23.2` |
| `BUILD_ALPINE_IMAGE` | Alpine image for build init/helper pods | `registry.abrban.com/proxy-dockerhub/library/alpine:3.19` |
| `BUILD_ALPINE_GIT_IMAGE` | Git-clone init container image | `registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0` |
| `BASE_IMAGE_REGISTRY` | Harbor prefix for Docker Hub images in generated Dockerfiles | `registry.abrban.com/proxy-dockerhub/library` |
| `UPLOAD_DIR` | Disk path for uploaded source archives | `./uploads` |
| `INGRESS_CLASS` | Ingress controller for app Ingress objects | `traefik` |
| `PLATFORM_DOMAIN` / `PREVIEW_BASE_DOMAIN` | Base domain for app subdomains / previews | `apps.cloudhost.local` / — |
+5
View File
@@ -75,6 +75,11 @@ REGISTRY_PASSWORD=registry_secret
# Build
BUILD_NAMESPACE=cloudhost-builds
BUILD_SERVICE_ACCOUNT=kaniko-builder
# Kaniko job images — defaults pull from Harbor proxy-cache when unset.
# KANIKO_IMAGE=registry.abrban.com/proxy-gcr/kaniko-project/executor:v1.23.2
# BUILD_ALPINE_IMAGE=registry.abrban.com/proxy-dockerhub/library/alpine:3.19
# BUILD_ALPINE_GIT_IMAGE=registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
# BASE_IMAGE_REGISTRY=registry.abrban.com/proxy-dockerhub/library
# Platform
# Public URL(s) of the frontend — used for CORS and to derive the platform/preview
@@ -113,3 +113,14 @@ PLATFORM_DOMAIN / preview domain from the first entry only. The panel host
{{- define "cloudhost-platform.frontendImage" -}}
{{- printf "%s:%s" .Values.images.frontend.repository .Values.images.frontend.tag }}
{{- end }}
{{- define "cloudhost-platform.buildEnv" -}}
- name: KANIKO_IMAGE
value: {{ .Values.build.images.kaniko | quote }}
- name: BUILD_ALPINE_IMAGE
value: {{ .Values.build.images.alpine | quote }}
- name: BUILD_ALPINE_GIT_IMAGE
value: {{ .Values.build.images.alpineGit | quote }}
- name: BASE_IMAGE_REGISTRY
value: {{ .Values.build.baseImageRegistry | quote }}
{{- end }}
@@ -100,6 +100,7 @@ spec:
key: cluster-kubeconfig-key
- name: FRONTEND_URL
value: {{ include "cloudhost-platform.corsOrigins" . | quote }}
{{- include "cloudhost-platform.buildEnv" . | nindent 12 }}
{{- range $key, $val := .Values.backend.env }}
- name: {{ $key }}
value: {{ $val | quote }}
@@ -22,6 +22,14 @@ images:
tag: "1.0.0"
pullPolicy: Always
# Build job images — override for clusters without Harbor proxy-cache.
build:
images:
kaniko: registry.example.com/proxy-gcr/kaniko-project/executor:v1.23.2
alpine: registry.example.com/proxy-dockerhub/library/alpine:3.19
alpineGit: registry.example.com/proxy-dockerhub/alpine/git:2.43.0
baseImageRegistry: registry.example.com/proxy-dockerhub/library
postgres:
password: "CHANGE_ME_STRONG_POSTGRES_PASSWORD"
# Pull secret for the mirrored postgres image
@@ -58,8 +66,6 @@ backend:
PLATFORM_DOMAIN: apps.example.com
REGISTRY_URL: registry.cloudhost-builds.svc.cluster.local:5000
REGISTRY_PULL_URL: registry.cloudhost-builds.svc.cluster.local:5000
# Mirror prefix for base images in generated Dockerfiles + managed services
BASE_IMAGE_REGISTRY: registry.example.com/mirror
# Elastic log-stack credentials (must match the logging namespace Secret)
ELASTIC_PASSWORD: "CHANGE_ME_ELASTIC_PASSWORD"
FLUENTBIT_PASSWORD: "CHANGE_ME_FLUENTBIT_PASSWORD"
@@ -28,6 +28,16 @@ images:
tag: "1.0.0"
pullPolicy: IfNotPresent
# Kaniko job images — defaults pull from Harbor proxy-cache.
# Override any line for a different registry/tag.
build:
images:
kaniko: registry.abrban.com/proxy-gcr/kaniko-project/executor:v1.23.2
alpine: registry.abrban.com/proxy-dockerhub/library/alpine:3.19
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
# Prefix for Docker Hub images in generated user-app Dockerfiles (node, php, …)
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
postgres:
enabled: true
database: cloudhost
+17 -11
View File
@@ -56,12 +56,6 @@ export class BuildService {
private readonly logger = new Logger(BuildService.name);
private readonly progressMap = new Map<string, BuildProgress>();
private readonly activeBuilds = new Map<string, ActiveBuildSession>();
/**
* Kaniko executor image. Pinned (not `:latest`) so it can be cached on the node
* with imagePullPolicy=IfNotPresent — avoids re-pulling the ~250MB image on every build.
*/
private readonly kanikoImage = process.env.KANIKO_IMAGE || 'gcr.io/kaniko-project/executor:v1.23.2';
constructor(
private configService: ConfigService,
private clustersService: ClustersService,
@@ -85,6 +79,18 @@ export class BuildService {
return `${prefix}/${image}`;
}
/** Kaniko executor — pinned (not :latest) for node-level caching. */
private getKanikoImage(): string {
return this.configService.get<string>('build.images.kaniko')!;
}
/** Init/helper container image — explicit Harbor ref or baseImage() fallback. */
private resolveBuildImage(kind: 'alpine' | 'alpineGit', dockerHubFallback: string): string {
const explicit = this.configService.get<string>(`build.images.${kind}`);
if (explicit) return explicit;
return this.baseImage(dockerHubFallback);
}
/**
* Git branch names come from users and end up in a shell command — accept
* only conservative ref characters and reject anything option-like.
@@ -548,7 +554,7 @@ export class BuildService {
// Add init container that unzips the source code from PVC
initContainers.push({
name: 'unzip-source',
image: this.baseImage('alpine:3.19'),
image: this.resolveBuildImage('alpine', 'alpine:3.19'),
imagePullPolicy: 'IfNotPresent',
command: [
'sh',
@@ -628,7 +634,7 @@ export class BuildService {
// Clone git repo into /workspace/source, then copy our generated Dockerfile
initContainers.push({
name: 'git-clone',
image: this.baseImage('alpine/git:2.43.0'),
image: this.resolveBuildImage('alpineGit', 'alpine/git:2.43.0'),
imagePullPolicy: 'IfNotPresent',
env: [
{ name: 'GIT_URL', value: app.gitUrl! },
@@ -679,7 +685,7 @@ export class BuildService {
// add an init container that creates empty source dir + copies Dockerfile
initContainers.push({
name: 'prepare-workspace',
image: this.baseImage('alpine:3.19'),
image: this.resolveBuildImage('alpine', 'alpine:3.19'),
imagePullPolicy: 'IfNotPresent',
command: [
'sh',
@@ -715,7 +721,7 @@ export class BuildService {
containers: [
{
name: 'kaniko',
image: this.kanikoImage,
image: this.getKanikoImage(),
imagePullPolicy: 'IfNotPresent',
args: kanikoArgs,
volumeMounts: kanikoVolumeMounts,
@@ -950,7 +956,7 @@ export class BuildService {
containers: [
{
name: 'helper',
image: this.baseImage('alpine:3.19'),
image: this.resolveBuildImage('alpine', 'alpine:3.19'),
imagePullPolicy: 'IfNotPresent',
command: ['sh', '-c', 'sleep 3600'],
volumeMounts: [{ name: 'source', mountPath: '/data' }],
+14 -5
View File
@@ -135,12 +135,21 @@ export default () => ({
namespace: process.env.BUILD_NAMESPACE || 'cloudhost-builds',
serviceAccount: process.env.BUILD_SERVICE_ACCOUNT || 'kaniko-builder',
/**
* Optional registry prefix for Docker Hub base images used in generated
* Dockerfiles and managed-service charts (e.g. "mirror.example.com" makes
* `node:20-alpine` → `mirror.example.com/node:20-alpine`). Useful when
* cluster nodes cannot reach docker.io directly.
* Registry prefix for Docker Hub base images used in generated Dockerfiles
* and managed-service charts (e.g. `node:20-alpine` →
* `registry.abrban.com/proxy-dockerhub/library/node:20-alpine`).
*/
baseImageRegistry: (process.env.BASE_IMAGE_REGISTRY || '').trim().replace(/\/+$/, ''),
baseImageRegistry: (process.env.BASE_IMAGE_REGISTRY || 'registry.abrban.com/proxy-dockerhub/library')
.trim()
.replace(/\/+$/, ''),
/** Full image refs for Kaniko jobs — override via Helm values or env. */
images: {
kaniko:
process.env.KANIKO_IMAGE ||
'registry.abrban.com/proxy-gcr/kaniko-project/executor:v1.23.2',
alpine: (process.env.BUILD_ALPINE_IMAGE || 'registry.abrban.com/proxy-dockerhub/library/alpine:3.19').trim(),
alpineGit: (process.env.BUILD_ALPINE_GIT_IMAGE || 'registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0').trim(),
},
/** Kaniko build container resources — tune for large images. */
kaniko: {
cpuRequest: process.env.KANIKO_CPU_REQUEST || '500m',
+8 -2
View File
@@ -28,6 +28,14 @@ images:
tag: "1.0.0" # ← CI overwrites on each deploy
pullPolicy: IfNotPresent
# Kaniko job images — Harbor proxy-cache (first pull is slow, no manual seed needed).
build:
images:
kaniko: registry.abrban.com/proxy-gcr/kaniko-project/executor:v1.23.2
alpine: registry.abrban.com/proxy-dockerhub/library/alpine:3.19
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
postgres:
enabled: true
database: cloudhost
@@ -98,8 +106,6 @@ backend:
PLATFORM_STORAGE_PROVISIONER: rancher.io/local-path
ELASTICSEARCH_HOST: elasticsearch.logging.svc.cluster.local
ELASTICSEARCH_AUTO_PORT_FORWARD: "false"
# Mirror prefix for user-app Dockerfiles and managed DB/Redis/RabbitMQ charts
BASE_IMAGE_REGISTRY: registry.abrban.com/proxy-dockerhub/library
# Must match elasticsearch-credentials Secret in logging namespace (not in Helm chart)
ELASTIC_PASSWORD: "CHANGE_VIA_SEALEDSECRET_OR_KUBECTL"
FLUENTBIT_PASSWORD: "CHANGE_VIA_SEALEDSECRET_OR_KUBECTL"