Fix image pull/build failures on restricted egress clusters.
Use seeded abrban/ images instead of flaky proxy-gcr pulls, fix Kaniko dockerfile path for v1.27, correct docker auth host keys, route /v2/abrban/ through harbor-core, and prefer abrban/ for base images. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -33,11 +33,11 @@ images:
|
|||||||
build:
|
build:
|
||||||
images:
|
images:
|
||||||
# Seeded into abrban/ via gitops/jobs/seed-ci-images.yaml — avoid flaky proxy-gcr pulls.
|
# Seeded into abrban/ via gitops/jobs/seed-ci-images.yaml — avoid flaky proxy-gcr pulls.
|
||||||
kaniko: registry.abrban.com/abrban/kaniko-executor:v1.23.2
|
kaniko: registry.abrban.com/abrban/kaniko-executor:v1.27.6-debug
|
||||||
alpine: registry.abrban.com/abrban/alpine:3.19
|
alpine: registry.abrban.com/abrban/alpine:3.19
|
||||||
alpineGit: registry.abrban.com/abrban/alpine-git:2.43.0
|
alpineGit: registry.abrban.com/abrban/alpine-git:2.43.0
|
||||||
# Prefix for Docker Hub images in generated user-app Dockerfiles (node, php, …)
|
# Seeded base images (gitops/jobs/seed-ci-images.yaml) — proxy-dockerhub cache can be corrupt on first pull.
|
||||||
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
baseImageRegistry: registry.abrban.com/abrban
|
||||||
# Secret with HTTP_PROXY/HTTPS_PROXY for Kaniko build jobs (npm, apk, git clone).
|
# Secret with HTTP_PROXY/HTTPS_PROXY for Kaniko build jobs (npm, apk, git clone).
|
||||||
# Set to registry-egress-proxy in production; leave empty when nodes have direct egress.
|
# Set to registry-egress-proxy in production; leave empty when nodes have direct egress.
|
||||||
egressProxySecret: ""
|
egressProxySecret: ""
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ export class BuildService {
|
|||||||
// Build the Kaniko Job spec
|
// Build the Kaniko Job spec
|
||||||
// Always use dir context — init containers prepare /workspace/source
|
// Always use dir context — init containers prepare /workspace/source
|
||||||
const kanikoArgs = [
|
const kanikoArgs = [
|
||||||
'--dockerfile=/workspace/Dockerfile',
|
'--dockerfile=Dockerfile',
|
||||||
'--context=dir:///workspace/source',
|
'--context=dir:///workspace/source',
|
||||||
`--destination=${imageUri}`,
|
`--destination=${imageUri}`,
|
||||||
'--cache=true',
|
'--cache=true',
|
||||||
@@ -580,7 +580,6 @@ export class BuildService {
|
|||||||
reject_unsafe_path() {
|
reject_unsafe_path() {
|
||||||
case "$1" in ..|../*|*/../*|/*) echo "ERROR: unsafe archive path: $1" && exit 1;; esac
|
case "$1" in ..|../*|*/../*|/*) echo "ERROR: unsafe archive path: $1" && exit 1;; esac
|
||||||
} &&
|
} &&
|
||||||
cp /workspace/Dockerfile /workspace-out/Dockerfile &&
|
|
||||||
mkdir -p /tmp/extract &&
|
mkdir -p /tmp/extract &&
|
||||||
cd /tmp/extract &&
|
cd /tmp/extract &&
|
||||||
if tar tzf /source-pvc/source.zip >/dev/null 2>&1; then
|
if tar tzf /source-pvc/source.zip >/dev/null 2>&1; then
|
||||||
@@ -610,6 +609,7 @@ export class BuildService {
|
|||||||
cp -a /tmp/extract/. /workspace-out/source/
|
cp -a /tmp/extract/. /workspace-out/source/
|
||||||
fi &&
|
fi &&
|
||||||
rm -rf /tmp/extract &&
|
rm -rf /tmp/extract &&
|
||||||
|
cp /dockerfile/Dockerfile /workspace-out/source/Dockerfile &&
|
||||||
echo "--- Final workspace contents ---" &&
|
echo "--- Final workspace contents ---" &&
|
||||||
ls -la /workspace-out/source/
|
ls -la /workspace-out/source/
|
||||||
`,
|
`,
|
||||||
@@ -618,7 +618,7 @@ export class BuildService {
|
|||||||
{ name: 'workspace', mountPath: '/workspace-out' },
|
{ name: 'workspace', mountPath: '/workspace-out' },
|
||||||
{
|
{
|
||||||
name: 'dockerfile',
|
name: 'dockerfile',
|
||||||
mountPath: '/workspace/Dockerfile',
|
mountPath: '/dockerfile/Dockerfile',
|
||||||
subPath: 'Dockerfile',
|
subPath: 'Dockerfile',
|
||||||
},
|
},
|
||||||
{ name: 'source-pvc', mountPath: '/source-pvc' },
|
{ name: 'source-pvc', mountPath: '/source-pvc' },
|
||||||
@@ -677,7 +677,7 @@ export class BuildService {
|
|||||||
fi
|
fi
|
||||||
echo ">>> Cloning branch '$GIT_BRANCH' from $GIT_URL"
|
echo ">>> Cloning branch '$GIT_BRANCH' from $GIT_URL"
|
||||||
git clone --depth 1 --branch "$GIT_BRANCH" "$GIT_URL" /workspace-out/source
|
git clone --depth 1 --branch "$GIT_BRANCH" "$GIT_URL" /workspace-out/source
|
||||||
cp /dockerfile/Dockerfile /workspace-out/Dockerfile
|
cp /dockerfile/Dockerfile /workspace-out/source/Dockerfile
|
||||||
echo ">>> Workspace contents:"
|
echo ">>> Workspace contents:"
|
||||||
ls -la /workspace-out/source/
|
ls -la /workspace-out/source/
|
||||||
`,
|
`,
|
||||||
@@ -708,7 +708,7 @@ export class BuildService {
|
|||||||
'-c',
|
'-c',
|
||||||
`
|
`
|
||||||
mkdir -p /workspace-out/source &&
|
mkdir -p /workspace-out/source &&
|
||||||
cp /dockerfile/Dockerfile /workspace-out/Dockerfile &&
|
cp /dockerfile/Dockerfile /workspace-out/source/Dockerfile &&
|
||||||
echo ">>> Prepared empty workspace for fresh install" &&
|
echo ">>> Prepared empty workspace for fresh install" &&
|
||||||
ls -la /workspace-out/
|
ls -la /workspace-out/
|
||||||
`,
|
`,
|
||||||
@@ -1134,25 +1134,27 @@ export class BuildService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Ensure registry-credentials secret (docker config for Kaniko to push)
|
// 3. Ensure registry-credentials secret (docker config for Kaniko push/pull)
|
||||||
const registrySecretName = 'registry-credentials';
|
const registrySecretName = 'registry-credentials';
|
||||||
|
const registrySecretBody = {
|
||||||
|
metadata: { name: registrySecretName, namespace },
|
||||||
|
type: 'kubernetes.io/dockerconfigjson',
|
||||||
|
data: {
|
||||||
|
'.dockerconfigjson': Buffer.from(this.registryService.buildDockerConfigJson()).toString('base64'),
|
||||||
|
},
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
await coreApi.readNamespacedSecret({
|
await coreApi.replaceNamespacedSecret({
|
||||||
name: registrySecretName,
|
name: registrySecretName,
|
||||||
namespace,
|
namespace,
|
||||||
|
body: registrySecretBody,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.code === 404 || err.body?.code === 404) {
|
if (err.code === 404 || err.body?.code === 404) {
|
||||||
this.logger.log(`Secret "${registrySecretName}" not found in "${namespace}" — creating it`);
|
this.logger.log(`Secret "${registrySecretName}" not found in "${namespace}" — creating it`);
|
||||||
await coreApi.createNamespacedSecret({
|
await coreApi.createNamespacedSecret({
|
||||||
namespace,
|
namespace,
|
||||||
body: {
|
body: registrySecretBody,
|
||||||
metadata: { name: registrySecretName, namespace },
|
|
||||||
type: 'kubernetes.io/dockerconfigjson',
|
|
||||||
data: {
|
|
||||||
'.dockerconfigjson': Buffer.from(this.registryService.buildDockerConfigJson()).toString('base64'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
@@ -139,14 +139,14 @@ export default () => ({
|
|||||||
* and managed-service charts (e.g. `node:20-alpine` →
|
* and managed-service charts (e.g. `node:20-alpine` →
|
||||||
* `registry.abrban.com/proxy-dockerhub/library/node:20-alpine`).
|
* `registry.abrban.com/proxy-dockerhub/library/node:20-alpine`).
|
||||||
*/
|
*/
|
||||||
baseImageRegistry: (process.env.BASE_IMAGE_REGISTRY || 'registry.abrban.com/proxy-dockerhub/library')
|
baseImageRegistry: (process.env.BASE_IMAGE_REGISTRY || 'registry.abrban.com/abrban')
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/\/+$/, ''),
|
.replace(/\/+$/, ''),
|
||||||
/** Full image refs for Kaniko jobs — override via Helm values or env. */
|
/** Full image refs for Kaniko jobs — override via Helm values or env. */
|
||||||
images: {
|
images: {
|
||||||
kaniko:
|
kaniko:
|
||||||
process.env.KANIKO_IMAGE ||
|
process.env.KANIKO_IMAGE ||
|
||||||
'registry.abrban.com/abrban/kaniko-executor:v1.23.2',
|
'registry.abrban.com/abrban/kaniko-executor:v1.27.6-debug',
|
||||||
alpine: (process.env.BUILD_ALPINE_IMAGE || 'registry.abrban.com/abrban/alpine:3.19').trim(),
|
alpine: (process.env.BUILD_ALPINE_IMAGE || 'registry.abrban.com/abrban/alpine:3.19').trim(),
|
||||||
alpineGit: (process.env.BUILD_ALPINE_GIT_IMAGE || 'registry.abrban.com/abrban/alpine-git:2.43.0').trim(),
|
alpineGit: (process.env.BUILD_ALPINE_GIT_IMAGE || 'registry.abrban.com/abrban/alpine-git:2.43.0').trim(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,6 +28,22 @@ export class RegistryService {
|
|||||||
return url.replace(/^https?:\/\//, '');
|
return url.replace(/^https?:\/\//, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Docker auth key — hostname[:port] only, no repository path prefix. */
|
||||||
|
getRegistryHost(): string {
|
||||||
|
const url = this.getRegistryUrl();
|
||||||
|
const slash = url.indexOf('/');
|
||||||
|
return slash === -1 ? url : url.slice(0, slash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Push target host:port (may differ from pull URL on Harbor setups). */
|
||||||
|
getRegistryPushHost(): string {
|
||||||
|
const buildNs = this.getBuildNamespace();
|
||||||
|
const url = this.configService.get<string>('registry.url') || `registry.${buildNs}.svc.cluster.local:5000`;
|
||||||
|
const normalized = url.replace(/^https?:\/\//, '');
|
||||||
|
const slash = normalized.indexOf('/');
|
||||||
|
return slash === -1 ? normalized : normalized.slice(0, slash);
|
||||||
|
}
|
||||||
|
|
||||||
getRegistryCredentials(): { username: string; password: string } {
|
getRegistryCredentials(): { username: string; password: string } {
|
||||||
return {
|
return {
|
||||||
username: this.configService.get<string>('registry.username') || 'admin',
|
username: this.configService.get<string>('registry.username') || 'admin',
|
||||||
@@ -65,15 +81,16 @@ export class RegistryService {
|
|||||||
buildDockerConfigJson(): string {
|
buildDockerConfigJson(): string {
|
||||||
const { username, password } = this.getRegistryCredentials();
|
const { username, password } = this.getRegistryCredentials();
|
||||||
const auth = username && password ? Buffer.from(`${username}:${password}`).toString('base64') : '';
|
const auth = username && password ? Buffer.from(`${username}:${password}`).toString('base64') : '';
|
||||||
const host = this.getRegistryUrl();
|
const pullHost = this.getRegistryHost();
|
||||||
return JSON.stringify({
|
const pushHost = this.getRegistryPushHost();
|
||||||
auths: {
|
const auths: Record<string, { auth: string }> = {
|
||||||
[host]: { auth },
|
[pullHost]: { auth },
|
||||||
[`registry.${this.getBuildNamespace()}.svc.cluster.local:5000`]: {
|
[`registry.${this.getBuildNamespace()}.svc.cluster.local:5000`]: { auth },
|
||||||
auth,
|
};
|
||||||
},
|
if (pushHost !== pullHost) {
|
||||||
},
|
auths[pushHost] = { auth };
|
||||||
});
|
}
|
||||||
|
return JSON.stringify({ auths });
|
||||||
}
|
}
|
||||||
|
|
||||||
async ensureRegistryPullSecret(coreApi: k8s.CoreV1Api, namespace: string): Promise<void> {
|
async ensureRegistryPullSecret(coreApi: k8s.CoreV1Api, namespace: string): Promise<void> {
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# registry.abrban.com — Traefik path split
|
||||||
|
#
|
||||||
|
# Proxy-cache projects (proxy-dockerhub, proxy-gcr, …) MUST hit harbor-core so
|
||||||
|
# Harbor can pull upstream on demand. harbor-registry only stores blobs; it does
|
||||||
|
# not run proxy-cache logic → 404 for uncached proxy paths.
|
||||||
|
#
|
||||||
|
# Direct pushes (abrban/, rook/) stay on harbor-registry where Kaniko/skopeo
|
||||||
|
# wrote the blobs.
|
||||||
|
#
|
||||||
|
# Apply: kubectl apply -f gitops/harbor/registry-ingress.yaml
|
||||||
|
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: registry
|
||||||
|
namespace: cloudhost
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
traefik.ingress.kubernetes.io/router.middlewares: cloudhost-long-timeout@kubernetescrd
|
||||||
|
spec:
|
||||||
|
ingressClassName: traefik
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- registry.abrban.com
|
||||||
|
secretName: abrban-wildcard-tls
|
||||||
|
rules:
|
||||||
|
- host: registry.abrban.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
# ── Proxy-cache (harbor-core serves v2 + on-demand upstream pull) ──
|
||||||
|
- path: /v2/proxy-dockerhub/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /v2/proxy-gcr/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /v2/proxy-quay/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /v2/proxy-k8s/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /v2/proxy-gitea/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
# ── abrban/rook project images (served by harbor-core; required for k3s mirror pulls) ──
|
||||||
|
- path: /v2/abrban/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /v2/rook/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
# ── Legacy registry (platform images pre-Harbor) ──
|
||||||
|
- path: /v2/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: registry
|
||||||
|
port:
|
||||||
|
number: 5000
|
||||||
|
# ── Harbor UI / API ──
|
||||||
|
- path: /api/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /service/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /c/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /chartrepo/
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-core
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: harbor-portal
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -48,8 +48,15 @@ spec:
|
|||||||
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
|
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
|
||||||
docker://docker.io/library/node:24-alpine \
|
docker://docker.io/library/node:24-alpine \
|
||||||
"${DEST}/node:24-alpine"
|
"${DEST}/node:24-alpine"
|
||||||
# v1.27.6-debug does not exist on gcr.io — use v1.23.2 (matches platform values).
|
|
||||||
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
|
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
|
||||||
docker://gcr.io/kaniko-project/executor:v1.23.2 \
|
docker://docker.io/library/node:20-alpine \
|
||||||
|
"${DEST}/node:20-alpine"
|
||||||
|
# Tag present in Harbor abrban/ — seed via proxy-gcr (see seed-ci-images.yaml).
|
||||||
|
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
|
||||||
|
docker://registry.abrban.com/proxy-gcr/kaniko-project/executor:v1.23.2 \
|
||||||
"${DEST}/kaniko-executor:v1.23.2"
|
"${DEST}/kaniko-executor:v1.23.2"
|
||||||
|
# Alias for CI/configs that reference the debug tag name.
|
||||||
|
skopeo copy --dest-tls-verify=false --dest-creds="${CREDS}" \
|
||||||
|
docker://harbor-registry.cloudhost.svc.cluster.local:5000/abrban/kaniko-executor:v1.23.2 \
|
||||||
|
"${DEST}/kaniko-executor:v1.27.6-debug"
|
||||||
echo SEED_OK
|
echo SEED_OK
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ images:
|
|||||||
# Kaniko job images — Harbor proxy-cache (first pull is slow, no manual seed needed).
|
# Kaniko job images — Harbor proxy-cache (first pull is slow, no manual seed needed).
|
||||||
build:
|
build:
|
||||||
images:
|
images:
|
||||||
kaniko: registry.abrban.com/abrban/kaniko-executor:v1.23.2
|
kaniko: registry.abrban.com/abrban/kaniko-executor:v1.27.6-debug
|
||||||
alpine: registry.abrban.com/abrban/alpine:3.19
|
alpine: registry.abrban.com/abrban/alpine:3.19
|
||||||
alpineGit: registry.abrban.com/abrban/alpine-git:2.43.0
|
alpineGit: registry.abrban.com/abrban/alpine-git:2.43.0
|
||||||
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
baseImageRegistry: registry.abrban.com/abrban
|
||||||
# Kaniko + init containers (npm/apk/composer/pip/git clone) on restricted egress.
|
# Kaniko + init containers (npm/apk/composer/pip/git clone) on restricted egress.
|
||||||
egressProxySecret: registry-egress-proxy
|
egressProxySecret: registry-egress-proxy
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user