From ef365b7bdf73793f8b61e84158bda8e2c4dce968 Mon Sep 17 00:00:00 2001 From: linhchi Date: Mon, 16 Mar 2026 13:55:52 +0700 Subject: [PATCH 01/12] feat: add chart registry --- kubernetes/docker-registry/Chart.yaml | 14 ++++++++ kubernetes/docker-registry/README.md | 28 +++++++++++++++ .../docker-registry/charts/common-1.4.3.tgz | Bin 0 -> 12906 bytes .../docker-registry/templates/config.yaml | 9 +++++ .../templates/registry_deployment.yaml | 33 ++++++++++++++++++ .../docker-registry/templates/service.yaml | 13 +++++++ kubernetes/docker-registry/values.yaml | 33 ++++++++++++++++++ 7 files changed, 130 insertions(+) create mode 100644 kubernetes/docker-registry/Chart.yaml create mode 100644 kubernetes/docker-registry/README.md create mode 100644 kubernetes/docker-registry/charts/common-1.4.3.tgz create mode 100644 kubernetes/docker-registry/templates/config.yaml create mode 100644 kubernetes/docker-registry/templates/registry_deployment.yaml create mode 100644 kubernetes/docker-registry/templates/service.yaml create mode 100644 kubernetes/docker-registry/values.yaml diff --git a/kubernetes/docker-registry/Chart.yaml b/kubernetes/docker-registry/Chart.yaml new file mode 100644 index 00000000..a57bafba --- /dev/null +++ b/kubernetes/docker-registry/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +annotations: + category: registry +name: docker-registry +description: Docker registry +engine: gotpl + +dependencies: + - name: common + repository: https://svtechnmaa.github.io/charts/artifacthub/ + version: 1.x.x + +version: 1.0.0 +appVersion: "v1.0.0" diff --git a/kubernetes/docker-registry/README.md b/kubernetes/docker-registry/README.md new file mode 100644 index 00000000..066740a1 --- /dev/null +++ b/kubernetes/docker-registry/README.md @@ -0,0 +1,28 @@ +# Shared Volume Chart + + + +1. Change `storageClass` in `values.yaml` to use seaweedfs or hostpath pv +2. IF: you use host path pv, the host path will be: `/opt/shared//-` +3. IF: you use seaweedfs, there are 2 ways to approach the data: + 1. API + - Filer path: `/api/ ` + 2. PV mount + - Filer path: `/csi/-` + - Host path: use `mount | grep -` to find mount path of the pv on the host. It will be something like `/var/lib/kubelet/plugins/kubernetes.io/csi/seaweedfs-csi-driver//globalmount` + +4. Tree level of Seaweedfs Filer + +``` ++--- +| +--- api + | +--- +| +--- csi + | +--- - ++--- +| +--- api + | +--- +| +--- csi + | +--- - +``` + diff --git a/kubernetes/docker-registry/charts/common-1.4.3.tgz b/kubernetes/docker-registry/charts/common-1.4.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..cc15bbaef47996328dbac8deaa5f7fd8185a98dc GIT binary patch literal 12906 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 U?&0(Q0{{U3{~b4LhyY9h0F(-O6aWAK literal 0 HcmV?d00001 diff --git a/kubernetes/docker-registry/templates/config.yaml b/kubernetes/docker-registry/templates/config.yaml new file mode 100644 index 00000000..009be74c --- /dev/null +++ b/kubernetes/docker-registry/templates/config.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} +data: + config.yml: | +{{ .Values.registryConfig | indent 4 }} + diff --git a/kubernetes/docker-registry/templates/registry_deployment.yaml b/kubernetes/docker-registry/templates/registry_deployment.yaml new file mode 100644 index 00000000..7b8f104c --- /dev/null +++ b/kubernetes/docker-registry/templates/registry_deployment.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: docker-registry +spec: + replicas: 1 + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: docker-registry + template: + metadata: + labels: + {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: docker-registry + spec: + containers: + - name: registry + image: {{ include "common.images.image" ( dict "imageRoot" .Values.image "global" .Values.global ) }} + ports: + - containerPort: 5000 + volumeMounts: + - name: config + mountPath: /etc/docker/registry + volumes: + - name: config + configMap: + name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + diff --git a/kubernetes/docker-registry/templates/service.yaml b/kubernetes/docker-registry/templates/service.yaml new file mode 100644 index 00000000..1a655140 --- /dev/null +++ b/kubernetes/docker-registry/templates/service.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "common.names.fullname" . }}-service +spec: + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + ports: + - name: http + port: 5000 + targetPort: 5000 + type: ClusterIP + diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml new file mode 100644 index 00000000..b844890a --- /dev/null +++ b/kubernetes/docker-registry/values.yaml @@ -0,0 +1,33 @@ +global: + imageRegistry: "" + +image: + registry: docker.io + repository: registry + tag: 2 + pullPolicy: IfNotPresent + +registryConfig: |- + version: 0.1 + + log: + level: info + + storage: + s3: + bucket: registry + region: default + regionendpoint: http://seaweedfs-s3.seaweedfs:8333 + secure: false + v4auth: true + accesskey: registry + secretkey: registry@123 + skipverify: true + rootdirectory: "/" + delete: + enabled: true + redirect: + disable: true + + http: + addr: :5000 From b04334de36e704a5413e707169d1396d1e7a9899 Mon Sep 17 00:00:00 2001 From: linhchi Date: Mon, 16 Mar 2026 14:08:53 +0700 Subject: [PATCH 02/12] fix: modify README.md --- kubernetes/docker-registry/README.md | 29 ++-------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/kubernetes/docker-registry/README.md b/kubernetes/docker-registry/README.md index 066740a1..eab9572c 100644 --- a/kubernetes/docker-registry/README.md +++ b/kubernetes/docker-registry/README.md @@ -1,28 +1,3 @@ -# Shared Volume Chart - - - -1. Change `storageClass` in `values.yaml` to use seaweedfs or hostpath pv -2. IF: you use host path pv, the host path will be: `/opt/shared//-` -3. IF: you use seaweedfs, there are 2 ways to approach the data: - 1. API - - Filer path: `/api/ ` - 2. PV mount - - Filer path: `/csi/-` - - Host path: use `mount | grep -` to find mount path of the pv on the host. It will be something like `/var/lib/kubelet/plugins/kubernetes.io/csi/seaweedfs-csi-driver//globalmount` - -4. Tree level of Seaweedfs Filer - -``` -+--- -| +--- api - | +--- -| +--- csi - | +--- - -+--- -| +--- api - | +--- -| +--- csi - | +--- - -``` +# Docker registry chart +Deploy registry chart using s3 storage from seaweedfs From 7ea2b575ec549558aee9c4626801d22473c96019 Mon Sep 17 00:00:00 2001 From: linhchi Date: Mon, 16 Mar 2026 14:11:59 +0700 Subject: [PATCH 03/12] fix: add comments --- kubernetes/docker-registry/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index b844890a..af854da8 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -20,8 +20,8 @@ registryConfig: |- regionendpoint: http://seaweedfs-s3.seaweedfs:8333 secure: false v4auth: true - accesskey: registry - secretkey: registry@123 + accesskey: registry # corresponding to your s3 key + secretkey: registry@123 # corresponding to your s3 key skipverify: true rootdirectory: "/" delete: From f26294cc7cb075f5ff1ebcfea91f115dda6e2631 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Sun, 12 Apr 2026 22:51:12 +0700 Subject: [PATCH 04/12] feat: add pvc --- kubernetes/docker-registry/templates/config.yaml | 1 + kubernetes/docker-registry/templates/pvc.yaml | 16 ++++++++++++++++ ...eployment.yaml => registry_statefuleset.yaml} | 8 +++++++- .../docker-registry/templates/service.yaml | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 kubernetes/docker-registry/templates/pvc.yaml rename kubernetes/docker-registry/templates/{registry_deployment.yaml => registry_statefuleset.yaml} (78%) diff --git a/kubernetes/docker-registry/templates/config.yaml b/kubernetes/docker-registry/templates/config.yaml index 009be74c..1bed3d65 100644 --- a/kubernetes/docker-registry/templates/config.yaml +++ b/kubernetes/docker-registry/templates/config.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace }} data: config.yml: | {{ .Values.registryConfig | indent 4 }} diff --git a/kubernetes/docker-registry/templates/pvc.yaml b/kubernetes/docker-registry/templates/pvc.yaml new file mode 100644 index 00000000..df80410c --- /dev/null +++ b/kubernetes/docker-registry/templates/pvc.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ printf "%s-storage" (include "common.names.fullname" .) }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: docker-registry + namespace: {{ .Release.Namespace }} +spec: + accessModes: + - ReadWriteOnce + storageClassName: seaweedfs-storage + resources: + requests: + storage: 6Gi \ No newline at end of file diff --git a/kubernetes/docker-registry/templates/registry_deployment.yaml b/kubernetes/docker-registry/templates/registry_statefuleset.yaml similarity index 78% rename from kubernetes/docker-registry/templates/registry_deployment.yaml rename to kubernetes/docker-registry/templates/registry_statefuleset.yaml index 7b8f104c..d72b7e69 100644 --- a/kubernetes/docker-registry/templates/registry_deployment.yaml +++ b/kubernetes/docker-registry/templates/registry_statefuleset.yaml @@ -1,11 +1,12 @@ --- apiVersion: apps/v1 -kind: Deployment +kind: StatefulSet metadata: name: {{ include "common.names.fullname" . }} labels: {{- include "common.labels.standard" . | nindent 4 }} app.kubernetes.io/component: docker-registry + namespace: {{ .Release.Namespace }} spec: replicas: 1 selector: @@ -26,8 +27,13 @@ spec: volumeMounts: - name: config mountPath: /etc/docker/registry + - name: storage + mountPath: /var/lib/registry volumes: - name: config configMap: name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + - name: storage + persistentVolumeClaim: + claimName: {{ printf "%s-storage" (include "common.names.fullname" .) }} diff --git a/kubernetes/docker-registry/templates/service.yaml b/kubernetes/docker-registry/templates/service.yaml index 1a655140..8a629474 100644 --- a/kubernetes/docker-registry/templates/service.yaml +++ b/kubernetes/docker-registry/templates/service.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: Service metadata: name: {{ include "common.names.fullname" . }}-service + namespace: {{ .Release.Namespace }} spec: selector: {{- include "common.labels.matchLabels" . | nindent 4 }} ports: From 2e62eb0ae25a1c1f735f5d045be34d38563afe18 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Mon, 13 Apr 2026 22:20:11 +0700 Subject: [PATCH 05/12] fix: change s3 accesskey --- kubernetes/docker-registry/values.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index af854da8..f1abe54a 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -2,8 +2,8 @@ global: imageRegistry: "" image: - registry: docker.io - repository: registry + registry: ghcr.io + repository: svtechnmaa/registry tag: 2 pullPolicy: IfNotPresent @@ -17,11 +17,11 @@ registryConfig: |- s3: bucket: registry region: default - regionendpoint: http://seaweedfs-s3.seaweedfs:8333 + regionendpoint: http://seaweedfs-s3.seaweedfs.svc.cluster.local:8333 secure: false v4auth: true - accesskey: registry # corresponding to your s3 key - secretkey: registry@123 # corresponding to your s3 key + accesskey: s3adminkey # corresponding to your s3 key + secretkey: s3adminkey@123 # corresponding to your s3 key skipverify: true rootdirectory: "/" delete: From 02d09633ebb8137de46d318cbc8f3d4b5e92efbb Mon Sep 17 00:00:00 2001 From: kiennkt Date: Wed, 15 Apr 2026 11:44:37 +0700 Subject: [PATCH 06/12] fix: change sts to daemonset, delete pvc, add readme.md --- kubernetes/docker-registry/README.md | 54 ++++++++++++++++++- kubernetes/docker-registry/templates/pvc.yaml | 16 ------ ...tefuleset.yaml => registry_daemonset.yaml} | 9 +--- 3 files changed, 53 insertions(+), 26 deletions(-) delete mode 100644 kubernetes/docker-registry/templates/pvc.yaml rename kubernetes/docker-registry/templates/{registry_statefuleset.yaml => registry_daemonset.yaml} (80%) diff --git a/kubernetes/docker-registry/README.md b/kubernetes/docker-registry/README.md index eab9572c..c9775a01 100644 --- a/kubernetes/docker-registry/README.md +++ b/kubernetes/docker-registry/README.md @@ -1,3 +1,53 @@ -# Docker registry chart +# Docker Private Registry (SeaweedFS S3 Backend) -Deploy registry chart using s3 storage from seaweedfs +This documentation guides you on how to manage and inspect a Private Registry with SeaweedFS S3 as the storage backend. + +--- + +## 1. View and Manage Images in Registry + + +### 1.1 By Curl +* **List all repositories (available images):** + + ```bash + # curl -s -X GET http://..svc.cluster.local:5000/v2/_catalog | jq . + ``` + Example: + + ```bash + curl -s -X GET http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/_catalog | jq . + ``` + --- +* **List tags of a specific image:** + + ```bash + # curl -X GET http://..svc.cluster.local:5000/v2//tags/list + ``` + Example: + + ```bash + curl -s -X GET http://private-registry-docker-registry-service.private-registry.svc.cluster.local:5000/v2//tags/list + ``` + +### 1.2 By GUI of seaweedfs +* Expose NodePort seaweedfs filer +* View images on GUI at folder ***http://:/buckets/registry/docker/registry/v2/repositories/*** +## 2. Pull Images using crictl + +* **Pull image** + + ```bash + crictl pull private.registry/svtechnmaa/: + ``` + + ***Note:*** `private.registry` is a *short name/alias* configured in containerd that points to the actual registry server at `registry-docker-registry-service.registry.svc.cluster.local:5000`. + + **Why this configuration is needed:** By default, containerd tries to connect to registries using HTTPS. However, this registry runs on HTTP. To allow HTTP connections, you must configure `private.registry` in containerd's configuration file. + + **Configuration example** in `/etc/containerd/certs.d/private.registry/hosts.toml`: + ```toml + server = "http://registry-docker-registry-service.registry.svc.cluster.local:5000" + ``` + + This allows you to pull images using the short name without needing to specify the full server address each time. \ No newline at end of file diff --git a/kubernetes/docker-registry/templates/pvc.yaml b/kubernetes/docker-registry/templates/pvc.yaml deleted file mode 100644 index df80410c..00000000 --- a/kubernetes/docker-registry/templates/pvc.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ printf "%s-storage" (include "common.names.fullname" .) }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: docker-registry - namespace: {{ .Release.Namespace }} -spec: - accessModes: - - ReadWriteOnce - storageClassName: seaweedfs-storage - resources: - requests: - storage: 6Gi \ No newline at end of file diff --git a/kubernetes/docker-registry/templates/registry_statefuleset.yaml b/kubernetes/docker-registry/templates/registry_daemonset.yaml similarity index 80% rename from kubernetes/docker-registry/templates/registry_statefuleset.yaml rename to kubernetes/docker-registry/templates/registry_daemonset.yaml index d72b7e69..3c81ad7e 100644 --- a/kubernetes/docker-registry/templates/registry_statefuleset.yaml +++ b/kubernetes/docker-registry/templates/registry_daemonset.yaml @@ -1,6 +1,6 @@ --- apiVersion: apps/v1 -kind: StatefulSet +kind: DaemonSet metadata: name: {{ include "common.names.fullname" . }} labels: @@ -8,7 +8,6 @@ metadata: app.kubernetes.io/component: docker-registry namespace: {{ .Release.Namespace }} spec: - replicas: 1 selector: matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} @@ -27,13 +26,7 @@ spec: volumeMounts: - name: config mountPath: /etc/docker/registry - - name: storage - mountPath: /var/lib/registry volumes: - name: config configMap: name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} - - name: storage - persistentVolumeClaim: - claimName: {{ printf "%s-storage" (include "common.names.fullname" .) }} - From a1c87d681bb226973bace7849a23ef71f039e3d6 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Tue, 21 Apr 2026 23:45:39 +0700 Subject: [PATCH 07/12] fix: change config and README.md --- kubernetes/docker-registry/README.md | 432 ++++++++++++++++++++++--- kubernetes/docker-registry/values.yaml | 113 ++++++- 2 files changed, 504 insertions(+), 41 deletions(-) diff --git a/kubernetes/docker-registry/README.md b/kubernetes/docker-registry/README.md index c9775a01..c2418cb5 100644 --- a/kubernetes/docker-registry/README.md +++ b/kubernetes/docker-registry/README.md @@ -1,53 +1,413 @@ -# Docker Private Registry (SeaweedFS S3 Backend) +# Docker Private Registry — SeaweedFS S3 Backend -This documentation guides you on how to manage and inspect a Private Registry with SeaweedFS S3 as the storage backend. +## Table of Contents +- [Architecture Overview](#architecture-overview) +- [How It Works — Full Flow](#how-it-works--full-flow) + - [Push Flow (skopeo copy to registry)](#1-push-flow-skopeo-copy-to-registry) + - [Pull Flow (containerd pull image)](#2-pull-flow-containerd-pull-image) + - [Registry ↔ SeaweedFS S3 Interaction](#3-registry--seaweedfs-s3-interaction) +- [Inspect Registry via curl](#inspect-registry-via-curl) +- [Inspect Registry via SeaweedFS GUI](#inspect-registry-via-seaweedfs-gui) +- [Pull Images using crictl](#pull-images-using-crictl) +- [Data Layout on SeaweedFS](#data-layout-on-seaweedfs) --- -## 1. View and Manage Images in Registry +## Architecture Overview +### Component Diagram -### 1.1 By Curl -* **List all repositories (available images):** +Three distinct services involved — each has a different role: - ```bash - # curl -s -X GET http://..svc.cluster.local:5000/v2/_catalog | jq . - ``` - Example: +``` + +-----------------------------------------------------------------------+ + | Kubernetes Cluster | + | | + | [ skopeo ] : CLI tool — copies images between registries | + | [ containerd ] : Container runtime — pulls images for pods | + | [ Docker Registry ] : Service — exposes image API on port 5000 | + | [ SeaweedFS S3 ] : Service — stores actual image data (blobs) | + | | + +-----------------------------------------------------------------------+ - ```bash - curl -s -X GET http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/_catalog | jq . - ``` - --- -* **List tags of a specific image:** + PUSH (skopeo) + +----------+ 1. copy image +-----------------+ S3 API +-------------+ + | ghcr.io |◄──────────────────| skopeo | | | + | source | (download blob) | | | SeaweedFS | + +----------+ | runs on any K8s | | S3 | + | node or host | | :8333 | + 2. push blob | | | | + ─────────────────►| Docker Registry |────────────► bucket: | + | ns: registry | | registry | + | :5000 | | | + +-----------------+ +-------------+ - ```bash - # curl -X GET http://..svc.cluster.local:5000/v2//tags/list - ``` - Example: + PULL (containerd / crictl) + +--------------+ 1. get manifest +-----------------+ +-------------+ + | containerd |──────────────────►| Docker Registry |──S3 API───►| | + | runs on each |◄──────────────────| ns: registry | | SeaweedFS | + | K8s node | manifest JSON | :5000 | | S3 | + | | +-----------------+ | :8333 | + | | 2. get blob → Registry replies: 307 Redirect | | + | |──────────────────────────────────────────────────► bucket: | + | |◄────────────────────────────────────────────────── registry | + | | blob content (downloaded directly from SeaweedFS)| | + +--------------+ +-------------+ - ```bash - curl -s -X GET http://private-registry-docker-registry-service.private-registry.svc.cluster.local:5000/v2//tags/list - ``` + ----------------------------------------------------------------------- + Docker Registry : registry-docker-registry-service.registry.svc.cluster.local:5000 + SeaweedFS S3 : seaweedfs-s3.seaweedfs.svc.cluster.local:8333 + containerd alias: private.registry → points to Docker Registry :5000 + ----------------------------------------------------------------------- +``` -### 1.2 By GUI of seaweedfs -* Expose NodePort seaweedfs filer -* View images on GUI at folder ***http://:/buckets/registry/docker/registry/v2/repositories/*** -## 2. Pull Images using crictl +> **Key point:** When pulling, containerd downloads blobs **directly from SeaweedFS** via a 307 redirect — the Registry only handles the manifest and redirect, it does not proxy the blob data itself. This keeps Registry resource usage low. -* **Pull image** +--- + +### Flow Diagram + +#### PUSH — skopeo copies image from ghcr.io into private registry + +``` + ghcr.io skopeo Docker Registry SeaweedFS S3 + ────────── ───────── ─────────────── ──────────── + │ │ │ │ + │ Step 1 │ │ │ + │ Download │ │ │ + │ manifest ◄──│ │ │ + │ & blobs │ │ │ + │ │ │ │ + │ │ Step 2 │ │ + │ │ HEAD blob │ HeadObject │ + │ │ (already exist?)│──────────────────► │ + │ │◄─────────────────│◄────── 404 ────────│ + │ │ 404 not found │ (blob missing) │ + │ │ │ │ + │ │ Step 3 │ │ + │ │ POST │ PutObject │ + │ │ (init session) │ (create temp file)│ + │ │─────────────────►│──────────────────► │ + │ │◄─────────────────│◄────── 200 ────────│ + │ │ 202 + uuid │ │ + │ │ │ │ + │ │ Step 4 │ │ + │ │ PATCH × N │ PutObject │ + │ │ (upload chunks) │ (write each chunk)│ + │ │─────────────────►│──────────────────► │ + │ │◄─────────────────│◄────── 200 ────────│ + │ │ 202 accepted │ (repeat per chunk)│ + │ │ │ │ + │ │ Step 5 │ │ + │ │ PUT │ CopyObject │ + │ │ (finalize blob) │ _uploads/ → blobs/│ + │ │─────────────────►│──────────────────► │ + │ │◄─────────────────│◄────── 200 ────────│ + │ │ 201 created │ (blob committed) │ + │ │ │ │ + │ │ Step 6 │ │ + │ │ PUT manifest │ PutObject │ + │ │─────────────────►│──────────────────► │ + │ │◄─────────────────│◄────── 200 ────────│ + │ │ 201 created │ (manifest saved) │ +``` + +#### PULL — containerd pulls image from private registry + +``` + containerd Docker Registry SeaweedFS S3 + ──────────── ─────────────── ──────────── + │ │ │ + │ Step 1 │ │ + │ GET manifest │ GetObject │ + │─────────────────►│──────────────────► │ + │◄─────────────────│◄───────────────────│ + │ manifest JSON │ │ + │ │ │ + │ Step 2 │ │ + │ HEAD blob │ HeadObject │ + │ (check size) │──────────────────► │ + │◄─────────────────│◄───────────────────│ + │ 200 + size │ │ + │ │ │ + │ Step 3 │ │ + │ GET blob │ │ + │─────────────────►│ │ + │◄─────────────────│ │ + │ 307 Redirect │ │ + │ Location: http://seaweedfs-s3:8333/. │ + │ │ │ + │ Step 4 — bypass Registry, go direct │ + │──────────────────────────────────────►│ + │◄──────────────────────────────────────│ + │ blob content (raw stream) │ + │ │ │ + │ (repeat Step 2→4 for each layer) │ +``` + +--- + +## How It Works — Full Flow + +### 1. Push Flow (skopeo copy to registry) + +``` +skopeo copy docker://ghcr.io/svtechnmaa/arangodb:3.12.2 \ + docker://registry:5000/svtechnmaa/arangodb:3.12.2 +``` + +``` +skopeo Registry SeaweedFS S3 + │ │ │ + │─── GET /v2/ │ │ + │ ← 200 OK │ │ + │ │ │ + │ ① Fetch source manifest │ │ + │─── GET ghcr.io/manifests/tag │ │ + │ ← manifest list │ │ + │ (multi-arch) │ │ + │ │ │ + │ ② Check each blob exists │ │ + │─── HEAD /v2/.../blobs/sha256:111 + │ ← 404 (not found) ─┼──► HEAD s3://registry/.../sha256:111 + │ │ ← 404 (not found) │ + │ │ │ + │ ③ Init upload session │ │ + │─── POST /v2/.../blobs/uploads/ + │ ─┼──► PUT s3://_uploads//data (empty) + │ ← 202 + uuid ─┼◄── 200 OK │ + │ │ │ + │ ④ Upload blob in chunks │ │ + │─── PATCH /uploads/ │ │ + │ [chunk 1: 0→128MB] ─┼──► PUT s3://_uploads//data + │ ← 202 ─┼◄── 200 OK │ + │─── PATCH /uploads/ │ │ + │ [chunk 2: 128→256MB] ─┼──► PUT s3://_uploads//data + │ ← 202 ─┼◄── 200 OK │ + │ │ │ + │ ⑤ Finalize upload │ │ + │─── PUT /uploads/ │ │ + │ ?digest=sha256:111 ─┼──► CopyObject │ + │ │ src: _uploads//data │ + │ │ dest: blobs/sha256:111 │ + │ │─── Delete _uploads//data │ + │ ← 201 Created │ │ + │ │ │ + │ ⑥ Push manifest │ │ + │─── PUT /v2/.../manifests/tag │ │ + │ ─┼──► PUT s3://.../manifests/tag │ + │ ← 201 Created │ │ +``` + +--- + +### 2. Pull Flow (containerd pull image) + +``` +crictl pull private.registry/svtechnmaa/arangodb:3.12.2 +``` + +``` +containerd Registry SeaweedFS S3 + │ │ │ + │ ① Fetch manifest │ │ + │─── GET /v2/.../manifests/3.12.2 + │ ─┼──► GET s3://.../manifests/3.12.2 + │ ← manifest JSON ─┼◄── blob content │ + │ │ │ + │ ② Check each layer │ │ + │─── HEAD /v2/.../blobs/sha256:111 + │ ─┼──► HEAD s3://.../blobs/sha256:111 + │ ← 200 + size ─┼◄── 200 OK │ + │ │ │ + │ ③ Download blob │ │ + │─── GET /v2/.../blobs/sha256:111 + │ │ │ + │ ← 307 Redirect │ (redirect: disable: false) │ + │ Location: http://seaweedfs-s3:8333/registry/... │ + │ │ │ + │─────────────────────────────────────────────────────────────►│ + │ GET http://seaweedfs-s3:8333/registry/.../blobs/sha256:111 │ + │ ← blob content (streamed directly from SeaweedFS) │ + │◄─────────────────────────────────────────────────────────────│ + │ │ │ + │ ④ Repeat for each layer │ │ +``` + +> **Note:** `redirect: disable: false` → containerd downloads blobs **directly from SeaweedFS** instead of going through the Registry. The Registry only returns a redirect URL and does not proxy the blob data — this significantly reduces CPU and memory usage on the Registry pod. + +--- + +### 3. Registry ↔ SeaweedFS S3 Interaction + +The Registry never stores data locally. Every read/write goes through the S3 driver to SeaweedFS. The table below maps each Registry API action to the exact S3 API call it triggers: + +| Triggered by | Registry API | S3 API called | What happens on SeaweedFS | +|---|---|---|---| +| skopeo HEAD blob | `HEAD /v2/.../blobs/` | `HeadObject` | Check if blob file exists in `blobs/sha256/` | +| skopeo POST init | `POST /v2/.../blobs/uploads/` | `PutObject` (empty) | Create empty temp file at `_uploads//data` | +| skopeo PATCH chunk | `PATCH /v2/.../blobs/uploads/` | `PutObject` (append) | Write chunk data into `_uploads//data` | +| skopeo PUT finalize (blob < 1GB) | `PUT /v2/.../blobs/uploads/` | `CopyObject` (single call) | Move `_uploads//data` → `blobs/sha256/` in one operation | +| skopeo PUT finalize (blob > 1GB) | `PUT /v2/.../blobs/uploads/` | `CreateMultipartUpload` → `CopyPart` × N → `CompleteMultipartUpload` | Move `_uploads//data` → `blobs/sha256/` in multiple parts | +| skopeo PUT manifest | `PUT /v2/.../manifests/` | `PutObject` | Write manifest JSON to `repositories//_manifests/` | +| containerd GET manifest | `GET /v2/.../manifests/` | `GetObject` | Read manifest JSON from `repositories//_manifests/` | +| containerd GET blob | `GET /v2/.../blobs/` | — (redirect) | Registry returns `307 Redirect` to pre-signed SeaweedFS URL; containerd downloads directly | +| curl DELETE image | `DELETE /v2/.../manifests/` | `DeleteObject` | Remove manifest file — blob files remain until garbage collection runs | +| curl GET catalog | `GET /v2/_catalog` | `ListObjects` | List all directories under `repositories/` | + +--- + +## Inspect Registry via curl + +### Catalog & Tags + +```bash +# List all repositories (images) available in the registry +curl -s http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/_catalog | jq . + +# Example response: +# { +# "repositories": [ +# "svtechnmaa/arangodb", +# "svtechnmaa/redis", +# ... +# ] +# } +``` + +```bash +# List all tags of a specific image +curl -s http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2//tags/list | jq . + +# Example: +curl -s http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/svtechnmaa/arangodb/tags/list | jq . + +# Example response: +# { +# "name": "svtechnmaa/arangodb", +# "tags": ["3.12.2"] +# } +``` + +--- + +### Manifest + +```bash +# Get the manifest of an image (returns manifest list if multi-arch) +curl -s \ + -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \ + -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2//manifests/ | jq . + +# Example: +curl -s \ + -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \ + -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/svtechnmaa/arangodb/manifests/3.12.2 | jq . +``` + +```bash +# Get the SHA256 digest of a manifest (needed for deletion) +curl -sI \ + -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2//manifests/ \ + | grep -i "docker-content-digest" + +# Example response: +# docker-content-digest: sha256:a1b2c3d4... +``` + +--- + +### Blob + +```bash +# Check whether a blob exists and get its size +curl -sI \ + http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2//blobs/ + +# Example: +curl -sI \ + http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/svtechnmaa/arangodb/blobs/sha256:abc123... + +# Response if blob exists: +# HTTP/1.1 200 OK +# Content-Length: 52428800 +# Docker-Content-Digest: sha256:abc123... + +# Response if blob does not exist: +# HTTP/1.1 404 Not Found +``` + +--- + +### Registry Health & Info + +```bash +# Check if registry is alive +curl -s http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/ +# Expected response: {} → registry is healthy + +# Check registry API version +curl -sI http://registry-docker-registry-service.registry.svc.cluster.local:5000/v2/ \ + | grep -i "docker-distribution-api-version" +# Expected response: docker-distribution-api-version: registry/2.0 +``` +--- + +## Inspect Registry via SeaweedFS GUI + +1. Expose the SeaweedFS filer port via port-forward: +```bash +kubectl -n seaweedfs port-forward svc/seaweedfs-filer 8888:8888 +``` + +2. Open in browser: +``` +http://localhost:8888/buckets/registry/registry-data/docker/registry/v2/repositories/ +``` + +Data layout on SeaweedFS: +``` +buckets/registry/registry-data/docker/registry/v2/ +├── blobs/ +│ └── sha256/ +│ ├── 11/sha256:111... ← layer blob (tar.gz compressed filesystem) +│ ├── 22/sha256:222... ← layer blob (tar.gz compressed filesystem) +│ └── ab/sha256:abc... ← config blob (image metadata JSON) +│ +└── repositories/ + └── svtechnmaa/ + ├── arangodb/ + │ ├── _manifests/ ← manifest index by tag and digest + │ ├── _layers/ ← links pointing back to blobs/ + │ └── _uploads/ ← temporary upload dir (empty when no upload is running) + └── redis/ + ├── _manifests/ + ├── _layers/ + └── _uploads/ +``` + +--- + +## Pull Images using crictl + +```bash +crictl pull private.registry/svtechnmaa/: +``` + +> **`private.registry`** is an alias configured in containerd that points to `registry-docker-registry-service.registry.svc.cluster.local:5000`. - ```bash - crictl pull private.registry/svtechnmaa/: - ``` +Configuration at `/etc/containerd/certs.d/private.registry/hosts.toml`: - ***Note:*** `private.registry` is a *short name/alias* configured in containerd that points to the actual registry server at `registry-docker-registry-service.registry.svc.cluster.local:5000`. +```toml +server = "http://registry-docker-registry-service.registry.svc.cluster.local:5000" - **Why this configuration is needed:** By default, containerd tries to connect to registries using HTTPS. However, this registry runs on HTTP. To allow HTTP connections, you must configure `private.registry` in containerd's configuration file. +[host."http://registry-docker-registry-service.registry.svc.cluster.local:5000"] + capabilities = ["pull", "resolve"] + skip_verify = true +``` - **Configuration example** in `/etc/containerd/certs.d/private.registry/hosts.toml`: - ```toml - server = "http://registry-docker-registry-service.registry.svc.cluster.local:5000" - ``` +> **Why this is required:** containerd defaults to HTTPS for all registries. This registry runs on plain HTTP — the `hosts.toml` file explicitly tells containerd to allow HTTP and skip TLS verification for this alias. - This allows you to pull images using the short name without needing to specify the full server address each time. \ No newline at end of file diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index f1abe54a..2e3ffee7 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -8,26 +8,129 @@ image: pullPolicy: IfNotPresent registryConfig: |- + # Docker Distribution Registry configuration + # Docs: https://distribution.github.io/distribution/about/configuration/ version: 0.1 log: + # Log level: error | warn | info | debug level: info storage: + cache: + # Cache blob metadata (digest, size, mediatype) in memory + # → Reduces HEAD requests to SeaweedFS S3 when skopeo checks if blob already exists + # → Does NOT cache blob content, only metadata + # ⚠ If running multiple registry replicas, cache is NOT shared between them + blobdescriptor: inmemory + s3: + # Name of the bucket on SeaweedFS where all registry data is stored + # Bucket must exist before registry starts bucket: registry - region: default + + # AWS region — required by AWS SDK even when not using real AWS + # SeaweedFS ignores this value, any string is accepted + region: us-east-1 + + # Override default AWS S3 endpoint to point at SeaweedFS S3 + # Required when using any S3-compatible service instead of real AWS regionendpoint: http://seaweedfs-s3.seaweedfs.svc.cluster.local:8333 + + # Use HTTP instead of HTTPS when talking to SeaweedFS + # Set to true if SeaweedFS is configured with TLS secure: false + + # Use AWS Signature Version 4 to sign all S3 requests + # SeaweedFS requires v4 — setting this to false will cause auth errors v4auth: true - accesskey: s3adminkey # corresponding to your s3 key - secretkey: s3adminkey@123 # corresponding to your s3 key + + # S3 access key configured in SeaweedFS s3 identity config + accesskey: s3adminkey + + # S3 secret key configured in SeaweedFS s3 identity config + secretkey: s3adminkey@123 + + # Skip TLS certificate verification + # Only applies when secure: true + # Safe to set true for internal cluster communication skipverify: true - rootdirectory: "/" + + # Root path prefix inside the bucket where all registry files are stored + # All data will be written to: s3://registry//docker/registry/v2/... + # ⚠ Changing this after data exists will make existing images invisible to registry + rootdirectory: / + + # Force path-style S3 URLs instead of virtual-hosted-style + # + # Virtual-hosted-style (forcepathstyle: false — default): + # http://registry.seaweedfs:8333/docker/registry/v2/... + # ↑ bucket name embedded in hostname → SeaweedFS cannot resolve + # + # Path-style (forcepathstyle: true): + # http://seaweedfs:8333/registry/docker/registry/v2/... + # ↑ bucket name in URL path → SeaweedFS resolves correctly + # + # ⚠ REQUIRED when using regionendpoint with SeaweedFS + # Without this → error: "Copy Source must mention the source bucket and key" + forcepathstyle: true + + # Size of each chunk when uploading a blob to S3 via multipart upload + # Default is 10MB → too many PATCH requests → increases chance of session loss + # 128MB → fewer PATCH requests per blob → more stable with SeaweedFS + # Minimum allowed by S3 spec: 5MB + # Formula: blob 256MB / chunksize 128MB = 2 PATCH requests total + chunksize: 134217728 # 128MB = 128 * 1024 * 1024 + + # Blobs SMALLER than this value use a single S3 CopyObject call to finalize + # Blobs LARGER than this value use S3 Multipart Copy (multiple CopyObject calls) + # Default is 32MB → almost all blobs trigger Multipart Copy → hits SeaweedFS CopyObject bug + # 1GB → most image layers (which are typically < 1GB) use single copy → avoids the bug + # Error prevented: "NoSuchUpload" and "Copy Source must mention source bucket and key" + multipartcopythresholdsize: 1073741824 # 1GB = 1024 * 1024 * 1024 + + # Size of each part when performing a Multipart Copy on SeaweedFS + # Only applies to blobs larger than multipartcopythresholdsize (> 1GB) + # Set equal to chunksize for consistency + multipartcopyChunksize: 134217728 # 128MB + + # Maximum number of concurrent Multipart Copy operations + # Default is 100 → 100 parallel CopyObject calls → SeaweedFS race condition + # 1 → serial execution → slower but stable with SeaweedFS + # Only affects blobs larger than multipartcopythresholdsize (> 1GB) + multipartcopyMaxConcurrency: 1 + delete: + # Allow blobs and manifests to be deleted via DELETE API + # Required for garbage collection to work correctly enabled: true + redirect: - disable: true + # Disable S3 pre-signed URL redirect for blob downloads + # When false (redirect enabled): registry returns a pre-signed S3 URL + # → client downloads blob directly from SeaweedFS + # → works only if the client can reach the S3 endpoint + # When true (redirect disabled): registry proxies the blob content itself + # → required when SeaweedFS is only accessible inside the cluster + disable: false # ⚠ set true if SeaweedFS is not reachable from outside cluster http: + # Address and port the registry listens on + # ":5000" = 0.0.0.0:5000 → all network interfaces + # Use "127.0.0.1:5000" to restrict to localhost only addr: :5000 + + # Secret key used to encrypt and decrypt upload session state (_state parameter) + # The _state token encodes: { UUID, Offset, StartedAt } for each active upload + # ⚠ Must be identical across all registry replicas in HA setup + # → if replicas have different secrets, PATCH/PUT from one replica + # cannot decrypt _state created by another → upload fails + # ⚠ Do not change while uploads are in progress + # → existing _state tokens cannot be decrypted → active uploads fail + secret: "registry_key_2026" + + headers: + # Security header returned in every HTTP response + # Prevents browsers from guessing (sniffing) the content-type of a response + # Best practice: always set this regardless of registry usage + X-Content-Type-Options: [nosniff] From a62e366d526ac706b105e7b76afb3bf26d385ffc Mon Sep 17 00:00:00 2001 From: kiennkt Date: Wed, 22 Apr 2026 10:04:54 +0700 Subject: [PATCH 08/12] chore: change README --- kubernetes/docker-registry/README.md | 91 ++++++++++++++++++---------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/kubernetes/docker-registry/README.md b/kubernetes/docker-registry/README.md index c2418cb5..c92c6a88 100644 --- a/kubernetes/docker-registry/README.md +++ b/kubernetes/docker-registry/README.md @@ -23,10 +23,10 @@ Three distinct services involved — each has a different role: +-----------------------------------------------------------------------+ | Kubernetes Cluster | | | - | [ skopeo ] : CLI tool — copies images between registries | - | [ containerd ] : Container runtime — pulls images for pods | - | [ Docker Registry ] : Service — exposes image API on port 5000 | - | [ SeaweedFS S3 ] : Service — stores actual image data (blobs) | + | [ skopeo ] : CLI tool — copies images between registries | + | [ containerd ] : Container runtime — pulls images for pods | + | [ Docker Registry ] : Service — exposes image API on port 5000 | + | [ SeaweedFS S3 ] : Service — stores actual image data (blobs) | | | +-----------------------------------------------------------------------+ @@ -42,17 +42,32 @@ Three distinct services involved — each has a different role: | :5000 | | | +-----------------+ +-------------+ - PULL (containerd / crictl) - +--------------+ 1. get manifest +-----------------+ +-------------+ - | containerd |──────────────────►| Docker Registry |──S3 API───►| | - | runs on each |◄──────────────────| ns: registry | | SeaweedFS | - | K8s node | manifest JSON | :5000 | | S3 | - | | +-----------------+ | :8333 | - | | 2. get blob → Registry replies: 307 Redirect | | - | |──────────────────────────────────────────────────► bucket: | - | |◄────────────────────────────────────────────────── registry | - | | blob content (downloaded directly from SeaweedFS)| | - +--------------+ +-------------+ + PULL (containerd — triggered via crictl) + + +--------------+ +-------------+ + | crictl | | | + | pull image | | SeaweedFS | + +--------------+ | S3 :8333 | + | | | + ▼ | bucket: | + +--------------+ Step 1. GET manifest +-----------------+ | registry | + | |─────────────────────────►| Docker Registry |──►| | + | containerd |◄─────────────────────────| ns: registry |◄─ | | + | (each node) | manifest JSON | :5000 | | | + | | | | | | + | | Step 2. GET blob | | | | + | |─────────────────────────►| | | | + | |◄─────────────────────────| | | | + | | 307 Redirect +-----------------+ | | + | | Location: http://seaweedfs-s3:8333/... | | + | | | | + | | Step 3. download blob directly (bypass Registry) | + | |───────────────────────────────────────────────►| | + | |◄───────────────────────────────────────────────| | + | | blob content (raw stream) | | + | | | | + | | (repeat Step 2→3 for each layer in manifest) | | + +--------------+ +-------------+ ----------------------------------------------------------------------- Docker Registry : registry-docker-registry-service.registry.svc.cluster.local:5000 @@ -110,6 +125,7 @@ Three distinct services involved — each has a different role: │ │─────────────────►│──────────────────► │ │ │◄─────────────────│◄────── 200 ────────│ │ │ 201 created │ (manifest saved) │ + ─────────────────────────────────────────────────────────────────────────── ``` #### PULL — containerd pulls image from private registry @@ -143,6 +159,7 @@ Three distinct services involved — each has a different role: │ blob content (raw stream) │ │ │ │ │ (repeat Step 2→4 for each layer) │ + ──────────────────────────────────────────────────────────── ``` --- @@ -170,20 +187,20 @@ skopeo Registry SeaweedFS S3 │ ② Check each blob exists │ │ │─── HEAD /v2/.../blobs/sha256:111 │ ← 404 (not found) ─┼──► HEAD s3://registry/.../sha256:111 - │ │ ← 404 (not found) │ + │ │ ← 404 (not found) │ │ │ │ │ ③ Init upload session │ │ │─── POST /v2/.../blobs/uploads/ │ ─┼──► PUT s3://_uploads//data (empty) - │ ← 202 + uuid ─┼◄── 200 OK │ + │ ← 202 + uuid ─┼◄── 200 OK │ │ │ │ │ ④ Upload blob in chunks │ │ │─── PATCH /uploads/ │ │ │ [chunk 1: 0→128MB] ─┼──► PUT s3://_uploads//data - │ ← 202 ─┼◄── 200 OK │ + │ ← 202 ─┼◄── 200 OK │ │─── PATCH /uploads/ │ │ │ [chunk 2: 128→256MB] ─┼──► PUT s3://_uploads//data - │ ← 202 ─┼◄── 200 OK │ + │ ← 202 ─┼◄── 200 OK │ │ │ │ │ ⑤ Finalize upload │ │ │─── PUT /uploads/ │ │ @@ -197,6 +214,7 @@ skopeo Registry SeaweedFS S3 │─── PUT /v2/.../manifests/tag │ │ │ ─┼──► PUT s3://.../manifests/tag │ │ ← 201 Created │ │ + ─────────────────────────────────────────────────────────────────────────── ``` --- @@ -213,25 +231,26 @@ containerd Registry SeaweedFS S3 │ ① Fetch manifest │ │ │─── GET /v2/.../manifests/3.12.2 │ ─┼──► GET s3://.../manifests/3.12.2 - │ ← manifest JSON ─┼◄── blob content │ + │ ← manifest JSON ─┼◄── blob content │ │ │ │ │ ② Check each layer │ │ │─── HEAD /v2/.../blobs/sha256:111 │ ─┼──► HEAD s3://.../blobs/sha256:111 - │ ← 200 + size ─┼◄── 200 OK │ + │ ← 200 + size ─┼◄── 200 OK │ │ │ │ │ ③ Download blob │ │ │─── GET /v2/.../blobs/sha256:111 │ │ │ - │ ← 307 Redirect │ (redirect: disable: false) │ - │ Location: http://seaweedfs-s3:8333/registry/... │ + │ ← 307 Redirect │ (redirect: disable: false) │ + │ Location: http://seaweedfs-s3:8333/registry/... │ │ │ │ - │─────────────────────────────────────────────────────────────►│ - │ GET http://seaweedfs-s3:8333/registry/.../blobs/sha256:111 │ - │ ← blob content (streamed directly from SeaweedFS) │ - │◄─────────────────────────────────────────────────────────────│ + │─────────────────────────────────────────────────────────────► │ + │ GET http://seaweedfs-s3:8333/registry/.../blobs/sha256:111 │ + │ ← blob content (streamed directly from SeaweedFS) │ + │◄───────────────────────────────────────────────────────────── │ │ │ │ │ ④ Repeat for each layer │ │ + ─────────────────────────────────────────────────────────────────────────── ``` > **Note:** `redirect: disable: false` → containerd downloads blobs **directly from SeaweedFS** instead of going through the Registry. The Registry only returns a redirect URL and does not proxy the blob data — this significantly reduces CPU and memory usage on the Registry pod. @@ -358,19 +377,29 @@ curl -sI http://registry-docker-registry-service.registry.svc.cluster.local:5000 ## Inspect Registry via SeaweedFS GUI -1. Expose the SeaweedFS filer port via port-forward: +1. Expose the SeaweedFS filer as a NodePort service: ```bash -kubectl -n seaweedfs port-forward svc/seaweedfs-filer 8888:8888 +kubectl -n seaweedfs expose svc/seaweedfs-filer \ + --name=seaweedfs-filer-nodeport \ + --type=NodePort \ + --port=8888 \ + --target-port=8888 + +# Get the assigned NodePort +kubectl -n seaweedfs get svc seaweedfs-filer-nodeport +# Example output: +# NAME TYPE CLUSTER-IP PORT(S) +# seaweedfs-filer-nodeport NodePort 10.96.x.x 8888:3XXXX/TCP ``` 2. Open in browser: ``` -http://localhost:8888/buckets/registry/registry-data/docker/registry/v2/repositories/ +http://:/buckets/registry/docker/registry/v2/repositories/ ``` Data layout on SeaweedFS: ``` -buckets/registry/registry-data/docker/registry/v2/ +buckets/registry/docker/registry/v2/ ├── blobs/ │ └── sha256/ │ ├── 11/sha256:111... ← layer blob (tar.gz compressed filesystem) From 67897d21f6f0e7a143980893556167a9cd36f1f9 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Tue, 28 Apr 2026 08:13:26 +0700 Subject: [PATCH 09/12] chore: refact template, values --- .../docker-registry/templates/config.yaml | 30 +++- .../templates/job-create-bucket.yaml | 28 ++++ kubernetes/docker-registry/values.yaml | 138 ++---------------- 3 files changed, 69 insertions(+), 127 deletions(-) create mode 100644 kubernetes/docker-registry/templates/job-create-bucket.yaml diff --git a/kubernetes/docker-registry/templates/config.yaml b/kubernetes/docker-registry/templates/config.yaml index 1bed3d65..2ea475ad 100644 --- a/kubernetes/docker-registry/templates/config.yaml +++ b/kubernetes/docker-registry/templates/config.yaml @@ -1,4 +1,3 @@ ---- apiVersion: v1 kind: ConfigMap metadata: @@ -6,5 +5,30 @@ metadata: namespace: {{ .Release.Namespace }} data: config.yml: | -{{ .Values.registryConfig | indent 4 }} - + version: 0.1 + log: + level: {{ .Values.registry.log.level | default "info" }} + storage: + cache: + blobdescriptor: inmemory + s3: + bucket: {{ .Values.registry.s3.bucket }} + accesskey: {{ .Values.registry.s3.accesskey }} + secretkey: {{ .Values.registry.s3.secretkey }} + region: {{ .Values.registry.s3.region | default "us-east-1" }} + regionendpoint: http://{{ .Values.seaweedfs.serviceName }}.{{ .Release.Namespace }}.svc.cluster.local:8333 + secure: {{ .Values.registry.s3.secure | default false }} + v4auth: true + rootdirectory: {{ .Values.registry.s3.rootdirectory | default "/" }} + forcepathstyle: true + chunksize: 134217728 + multipartcopythresholdsize: 1073741824 + multipartcopyChunksize: 134217728 + multipartcopyMaxConcurrency: 1 + redirect: + disable: {{ .Values.registry.storage.redirect.disable | default false }} + http: + addr: :5000 + secret: "registry_key_2026" + headers: + X-Content-Type-Options: [nosniff] diff --git a/kubernetes/docker-registry/templates/job-create-bucket.yaml b/kubernetes/docker-registry/templates/job-create-bucket.yaml new file mode 100644 index 00000000..33e74721 --- /dev/null +++ b/kubernetes/docker-registry/templates/job-create-bucket.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "common.names.fullname" . }}-create-bucket + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-5" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-bucket + image: minio/mc:latest + command: + - /bin/sh + - -c + - | + mc alias set seaweedfs {{ .Values.seaweedfs.endpoint }} \ + {{ .Values.seaweedfs.accesskey }} \ + {{ .Values.seaweedfs.secretkey }} + + {{- range .Values.seaweedfs.buckets }} + mc mb --ignore-existing seaweedfs/{{ . }} + {{- end }} + diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index 2e3ffee7..42dd21c6 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -2,135 +2,25 @@ global: imageRegistry: "" image: - registry: ghcr.io - repository: svtechnmaa/registry - tag: 2 + registry: docker.io + repository: registry + tag: 2.6 pullPolicy: IfNotPresent -registryConfig: |- - # Docker Distribution Registry configuration - # Docs: https://distribution.github.io/distribution/about/configuration/ - version: 0.1 - +registry: log: - # Log level: error | warn | info | debug level: info - + s3: + bucket: registry + region: us-east-1 + secure: false + rootdirectory: / + accesskey: "" + secretkey: "" storage: - cache: - # Cache blob metadata (digest, size, mediatype) in memory - # → Reduces HEAD requests to SeaweedFS S3 when skopeo checks if blob already exists - # → Does NOT cache blob content, only metadata - # ⚠ If running multiple registry replicas, cache is NOT shared between them - blobdescriptor: inmemory - - s3: - # Name of the bucket on SeaweedFS where all registry data is stored - # Bucket must exist before registry starts - bucket: registry - - # AWS region — required by AWS SDK even when not using real AWS - # SeaweedFS ignores this value, any string is accepted - region: us-east-1 - - # Override default AWS S3 endpoint to point at SeaweedFS S3 - # Required when using any S3-compatible service instead of real AWS - regionendpoint: http://seaweedfs-s3.seaweedfs.svc.cluster.local:8333 - - # Use HTTP instead of HTTPS when talking to SeaweedFS - # Set to true if SeaweedFS is configured with TLS - secure: false - - # Use AWS Signature Version 4 to sign all S3 requests - # SeaweedFS requires v4 — setting this to false will cause auth errors - v4auth: true - - # S3 access key configured in SeaweedFS s3 identity config - accesskey: s3adminkey - - # S3 secret key configured in SeaweedFS s3 identity config - secretkey: s3adminkey@123 - - # Skip TLS certificate verification - # Only applies when secure: true - # Safe to set true for internal cluster communication - skipverify: true - - # Root path prefix inside the bucket where all registry files are stored - # All data will be written to: s3://registry//docker/registry/v2/... - # ⚠ Changing this after data exists will make existing images invisible to registry - rootdirectory: / - - # Force path-style S3 URLs instead of virtual-hosted-style - # - # Virtual-hosted-style (forcepathstyle: false — default): - # http://registry.seaweedfs:8333/docker/registry/v2/... - # ↑ bucket name embedded in hostname → SeaweedFS cannot resolve - # - # Path-style (forcepathstyle: true): - # http://seaweedfs:8333/registry/docker/registry/v2/... - # ↑ bucket name in URL path → SeaweedFS resolves correctly - # - # ⚠ REQUIRED when using regionendpoint with SeaweedFS - # Without this → error: "Copy Source must mention the source bucket and key" - forcepathstyle: true - - # Size of each chunk when uploading a blob to S3 via multipart upload - # Default is 10MB → too many PATCH requests → increases chance of session loss - # 128MB → fewer PATCH requests per blob → more stable with SeaweedFS - # Minimum allowed by S3 spec: 5MB - # Formula: blob 256MB / chunksize 128MB = 2 PATCH requests total - chunksize: 134217728 # 128MB = 128 * 1024 * 1024 - - # Blobs SMALLER than this value use a single S3 CopyObject call to finalize - # Blobs LARGER than this value use S3 Multipart Copy (multiple CopyObject calls) - # Default is 32MB → almost all blobs trigger Multipart Copy → hits SeaweedFS CopyObject bug - # 1GB → most image layers (which are typically < 1GB) use single copy → avoids the bug - # Error prevented: "NoSuchUpload" and "Copy Source must mention source bucket and key" - multipartcopythresholdsize: 1073741824 # 1GB = 1024 * 1024 * 1024 - - # Size of each part when performing a Multipart Copy on SeaweedFS - # Only applies to blobs larger than multipartcopythresholdsize (> 1GB) - # Set equal to chunksize for consistency - multipartcopyChunksize: 134217728 # 128MB - - # Maximum number of concurrent Multipart Copy operations - # Default is 100 → 100 parallel CopyObject calls → SeaweedFS race condition - # 1 → serial execution → slower but stable with SeaweedFS - # Only affects blobs larger than multipartcopythresholdsize (> 1GB) - multipartcopyMaxConcurrency: 1 - - delete: - # Allow blobs and manifests to be deleted via DELETE API - # Required for garbage collection to work correctly - enabled: true - redirect: - # Disable S3 pre-signed URL redirect for blob downloads - # When false (redirect enabled): registry returns a pre-signed S3 URL - # → client downloads blob directly from SeaweedFS - # → works only if the client can reach the S3 endpoint - # When true (redirect disabled): registry proxies the blob content itself - # → required when SeaweedFS is only accessible inside the cluster - disable: false # ⚠ set true if SeaweedFS is not reachable from outside cluster - - http: - # Address and port the registry listens on - # ":5000" = 0.0.0.0:5000 → all network interfaces - # Use "127.0.0.1:5000" to restrict to localhost only - addr: :5000 + disable: false - # Secret key used to encrypt and decrypt upload session state (_state parameter) - # The _state token encodes: { UUID, Offset, StartedAt } for each active upload - # ⚠ Must be identical across all registry replicas in HA setup - # → if replicas have different secrets, PATCH/PUT from one replica - # cannot decrypt _state created by another → upload fails - # ⚠ Do not change while uploads are in progress - # → existing _state tokens cannot be decrypted → active uploads fail - secret: "registry_key_2026" +seaweedfs: + serviceName: seaweedfs-s3 - headers: - # Security header returned in every HTTP response - # Prevents browsers from guessing (sniffing) the content-type of a response - # Best practice: always set this regardless of registry usage - X-Content-Type-Options: [nosniff] From 666cd8e518560f9fb0c0b83f36ddb208c5a8d5d9 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Tue, 28 Apr 2026 17:28:09 +0700 Subject: [PATCH 10/12] fix: change job, image, create image_pull_secret, refact templates --- .../docker-registry/templates/_hepers.tpl | 10 ++ .../docker-registry/templates/config.yaml | 30 +++--- .../templates/image_pull_secret.yaml | 17 ++++ .../templates/job-create-bucket.yaml | 28 ------ .../templates/job_create_bucket.yaml | 58 +++++++++++ .../templates/registry_daemonset.yaml | 83 +++++++++++++--- kubernetes/docker-registry/values.yaml | 98 ++++++++++++++++++- 7 files changed, 268 insertions(+), 56 deletions(-) create mode 100644 kubernetes/docker-registry/templates/_hepers.tpl create mode 100644 kubernetes/docker-registry/templates/image_pull_secret.yaml delete mode 100644 kubernetes/docker-registry/templates/job-create-bucket.yaml create mode 100644 kubernetes/docker-registry/templates/job_create_bucket.yaml diff --git a/kubernetes/docker-registry/templates/_hepers.tpl b/kubernetes/docker-registry/templates/_hepers.tpl new file mode 100644 index 00000000..c618700c --- /dev/null +++ b/kubernetes/docker-registry/templates/_hepers.tpl @@ -0,0 +1,10 @@ +{{/* +SeaweedFS S3 endpoint +*/}} +{{- define "registry.s3.endpoint" -}} +{{- $protocol := .Values.seaweedfs.protocol | default "http" -}} +{{- $svc := .Values.seaweedfs.serviceName | default "seaweedfs-s3" -}} +{{- $ns := .Values.seaweedfs.namespace | default .Release.Namespace -}} +{{- $port := .Values.seaweedfs.port | default 8333 -}} +{{- printf "%s://%s.%s.svc.cluster.local:%v" $protocol $svc $ns $port -}} +{{- end }} \ No newline at end of file diff --git a/kubernetes/docker-registry/templates/config.yaml b/kubernetes/docker-registry/templates/config.yaml index 2ea475ad..3dd46e3c 100644 --- a/kubernetes/docker-registry/templates/config.yaml +++ b/kubernetes/docker-registry/templates/config.yaml @@ -3,6 +3,9 @@ kind: ConfigMap metadata: name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} namespace: {{ .Release.Namespace }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: docker-registry data: config.yml: | version: 0.1 @@ -11,24 +14,25 @@ data: storage: cache: blobdescriptor: inmemory + {{- $endpoint := .Values.registry.s3.endpointOverride | default (include "registry.s3.endpoint" .) }} s3: - bucket: {{ .Values.registry.s3.bucket }} - accesskey: {{ .Values.registry.s3.accesskey }} - secretkey: {{ .Values.registry.s3.secretkey }} + bucket: {{ .Values.registry.s3.bucket | required "registry.s3.bucket is required" }} + accesskey: {{ .Values.registry.s3.accesskey | required "registry.s3.accesskey is required" }} + secretkey: {{ .Values.registry.s3.secretkey | required "registry.s3.secretkey is required" }} region: {{ .Values.registry.s3.region | default "us-east-1" }} - regionendpoint: http://{{ .Values.seaweedfs.serviceName }}.{{ .Release.Namespace }}.svc.cluster.local:8333 + regionendpoint: {{ $endpoint }} secure: {{ .Values.registry.s3.secure | default false }} - v4auth: true + v4auth: {{ .Values.registry.s3.v4auth | default true }} rootdirectory: {{ .Values.registry.s3.rootdirectory | default "/" }} - forcepathstyle: true - chunksize: 134217728 - multipartcopythresholdsize: 1073741824 - multipartcopyChunksize: 134217728 - multipartcopyMaxConcurrency: 1 + forcepathstyle: {{ .Values.registry.s3.forcepathstyle | default true }} + chunksize: {{ .Values.registry.s3.chunksize | default 134217728 | int64 }} + multipartcopythresholdsize: {{ .Values.registry.s3.multipartcopythresholdsize | default 1073741824 | int64 }} + multipartcopyChunksize: {{ .Values.registry.s3.multipartcopyChunksize | default 134217728 | int64 }} + multipartcopyMaxConcurrency: {{ .Values.registry.s3.multipartcopyMaxConcurrency | default 1 | int }} redirect: disable: {{ .Values.registry.storage.redirect.disable | default false }} http: - addr: :5000 - secret: "registry_key_2026" + addr: {{ .Values.registry.http.addr | default ":5000" }} + secret: {{ .Values.registry.http.secret | required "registry.http.secret is required" }} headers: - X-Content-Type-Options: [nosniff] + X-Content-Type-Options: {{ .Values.registry.http.headers.XContentTypeOptions | default "[nosniff]" }} diff --git a/kubernetes/docker-registry/templates/image_pull_secret.yaml b/kubernetes/docker-registry/templates/image_pull_secret.yaml new file mode 100644 index 00000000..3e5fa1dc --- /dev/null +++ b/kubernetes/docker-registry/templates/image_pull_secret.yaml @@ -0,0 +1,17 @@ +# templates/imagepullsecret.yaml +{{- if .Values.imagePullSecret.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }}-pull-secret + namespace: {{ .Release.Namespace }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-10" + helm.sh/hook-delete-policy: before-hook-creation +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ printf `{"auths":{"%s":{"username":"%s","password":"%s","auth":"%s"}}}` .Values.imagePullSecret.registry .Values.imagePullSecret.username .Values.imagePullSecret.password (printf "%s:%s" .Values.imagePullSecret.username .Values.imagePullSecret.password | b64enc) | b64enc }} +{{- end }} \ No newline at end of file diff --git a/kubernetes/docker-registry/templates/job-create-bucket.yaml b/kubernetes/docker-registry/templates/job-create-bucket.yaml deleted file mode 100644 index 33e74721..00000000 --- a/kubernetes/docker-registry/templates/job-create-bucket.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "common.names.fullname" . }}-create-bucket - namespace: {{ .Release.Namespace }} - annotations: - helm.sh/hook: pre-install,pre-upgrade - helm.sh/hook-weight: "-5" - helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded -spec: - template: - spec: - restartPolicy: OnFailure - containers: - - name: create-bucket - image: minio/mc:latest - command: - - /bin/sh - - -c - - | - mc alias set seaweedfs {{ .Values.seaweedfs.endpoint }} \ - {{ .Values.seaweedfs.accesskey }} \ - {{ .Values.seaweedfs.secretkey }} - - {{- range .Values.seaweedfs.buckets }} - mc mb --ignore-existing seaweedfs/{{ . }} - {{- end }} - diff --git a/kubernetes/docker-registry/templates/job_create_bucket.yaml b/kubernetes/docker-registry/templates/job_create_bucket.yaml new file mode 100644 index 00000000..cb30bc80 --- /dev/null +++ b/kubernetes/docker-registry/templates/job_create_bucket.yaml @@ -0,0 +1,58 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "common.names.fullname" . }}-create-bucket + namespace: {{ .Release.Namespace }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: create-bucket + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-5" + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded,hook-failed +spec: + {{- if .Values.createBucket.activeDeadlineSeconds }} + activeDeadlineSeconds: {{ .Values.createBucket.activeDeadlineSeconds }} + {{- end }} + template: + metadata: + labels: + {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: create-bucket + spec: + restartPolicy: OnFailure + {{- if .Values.imagePullSecret.enabled }} + imagePullSecrets: + - name: {{ include "common.names.fullname" . }}-pull-secret + {{- end }} + containers: + - name: create-bucket + image: {{ .Values.createBucket.image.registry }}/{{ .Values.createBucket.image.repository }}:{{ .Values.createBucket.image.tag }} + imagePullPolicy: {{ .Values.createBucket.image.pullPolicy }} + {{- with .Values.createBucket.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- $endpoint := .Values.registry.s3.endpointOverride | default (include "registry.s3.endpoint" .) }} + env: + - name: S3_ENDPOINT + value: {{ $endpoint | quote }} + - name: S3_BUCKET + value: {{ .Values.registry.s3.bucket | quote }} + - name: S3_ACCESS_KEY + value: {{ .Values.registry.s3.accesskey | quote }} + - name: S3_SECRET_KEY + value: {{ .Values.registry.s3.secretkey | quote }} + command: + - /bin/sh + - -c + - | + set -e + + echo "Setting up mc alias..." + mc alias set seaweedfs "${S3_ENDPOINT}" "${S3_ACCESS_KEY}" "${S3_SECRET_KEY}" + + echo "Creating bucket ${S3_BUCKET} if not exists..." + mc mb --ignore-existing "seaweedfs/${S3_BUCKET}" + + echo "Done!" \ No newline at end of file diff --git a/kubernetes/docker-registry/templates/registry_daemonset.yaml b/kubernetes/docker-registry/templates/registry_daemonset.yaml index 3c81ad7e..3d82194a 100644 --- a/kubernetes/docker-registry/templates/registry_daemonset.yaml +++ b/kubernetes/docker-registry/templates/registry_daemonset.yaml @@ -1,12 +1,11 @@ ---- apiVersion: apps/v1 kind: DaemonSet metadata: name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace }} labels: {{- include "common.labels.standard" . | nindent 4 }} app.kubernetes.io/component: docker-registry - namespace: {{ .Release.Namespace }} spec: selector: matchLabels: @@ -17,16 +16,76 @@ spec: labels: {{- include "common.labels.standard" . | nindent 8 }} app.kubernetes.io/component: docker-registry + {{- with .Values.registry.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.registry.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} spec: + {{- if .Values.imagePullSecret.enabled }} + imagePullSecrets: + - name: {{ include "common.names.fullname" . }}-pull-secret + {{- end }} containers: - - name: registry - image: {{ include "common.images.image" ( dict "imageRoot" .Values.image "global" .Values.global ) }} - ports: - - containerPort: 5000 - volumeMounts: - - name: config - mountPath: /etc/docker/registry + - name: registry + image: {{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + + ports: + - name: http + containerPort: 5000 + protocol: TCP + + {{- with .Values.registry.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + + {{- if .Values.registry.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: {{ .Values.registry.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.registry.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.registry.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.registry.livenessProbe.failureThreshold }} + {{- end }} + + {{- if .Values.registry.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: {{ .Values.registry.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.registry.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.registry.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.registry.readinessProbe.failureThreshold }} + {{- end }} + + volumeMounts: + - name: config + mountPath: /etc/docker/registry + readOnly: true + volumes: - - name: config - configMap: - name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + - name: config + configMap: + name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + + {{- with .Values.registry.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.registry.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.registry.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index 42dd21c6..966fdeed 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -1,26 +1,118 @@ global: imageRegistry: "" +imagePullSecret: + enabled: true + registry: ghcr.io + username: "" + password: "" + image: - registry: docker.io - repository: registry + registry: ghcr.io + repository: svtechnmaa/registry tag: 2.6 pullPolicy: IfNotPresent +# --------------------------------------------------------------------------- +# Registry Configuration +# --------------------------------------------------------------------------- registry: log: level: info + s3: bucket: registry region: us-east-1 secure: false rootdirectory: / + endpointOverride: "" accesskey: "" secretkey: "" + v4auth: true + forcepathstyle: true + chunksize: 134217728 + multipartcopythresholdsize: 1073741824 + multipartcopyChunksize: 134217728 + multipartcopyMaxConcurrency: 1 + storage: redirect: disable: false + http: + addr: ":5000" + secret: "registry_key_2026" + headers: + X-Content-Type-Options: "[nosniff]" + + # Liveness / Readiness + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + # Resource limits + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + + # Node scheduling + nodeSelector: {} + tolerations: [] + affinity: {} + + # Extra annotations/labels cho pods + podAnnotations: {} + podLabels: {} + +# --------------------------------------------------------------------------- +# Service +# --------------------------------------------------------------------------- +service: + type: ClusterIP + port: 5000 + nodePort: "" + annotations: {} + +# --------------------------------------------------------------------------- +# Job: Create Bucket +# --------------------------------------------------------------------------- +createBucket: + image: + registry: ghcr.io + repository: svtechnmaa/minio-client + tag: "2025-08-13" + pullPolicy: IfNotPresent + + # Timeout job (seconds) + activeDeadlineSeconds: 120 + + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + +# --------------------------------------------------------------------------- +# SeaweedFS (reference) +# --------------------------------------------------------------------------- seaweedfs: serviceName: seaweedfs-s3 - + namespace: seaweedfs + port: 8333 + protocol: http \ No newline at end of file From 27eee65ec434adf08105098784d8f5de97e47d65 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Mon, 4 May 2026 10:19:45 +0700 Subject: [PATCH 11/12] fix: add comments to each parameter in configmap --- .../docker-registry/templates/config.yaml | 80 +++++++++++++++++++ .../docker-registry/templates/service.yaml | 4 +- kubernetes/docker-registry/values.yaml | 8 +- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/kubernetes/docker-registry/templates/config.yaml b/kubernetes/docker-registry/templates/config.yaml index 3dd46e3c..a5076dc4 100644 --- a/kubernetes/docker-registry/templates/config.yaml +++ b/kubernetes/docker-registry/templates/config.yaml @@ -8,31 +8,111 @@ metadata: app.kubernetes.io/component: docker-registry data: config.yml: | + # Docker Distribution Registry configuration + # Docs: https://distribution.github.io/distribution/about/configuration/ version: 0.1 log: + # Log level: error | warn | info | debug level: {{ .Values.registry.log.level | default "info" }} storage: cache: + # Cache blob metadata (digest, size, mediatype) in memory + # → Reduces HEAD requests to SeaweedFS S3 when skopeo checks if blob already exists + # → Does NOT cache blob content, only metadata + # ⚠ If running multiple registry replicas, cache is NOT shared between them blobdescriptor: inmemory {{- $endpoint := .Values.registry.s3.endpointOverride | default (include "registry.s3.endpoint" .) }} s3: + # Name of the bucket on SeaweedFS where all registry data is stored + # Bucket must exist before registry starts bucket: {{ .Values.registry.s3.bucket | required "registry.s3.bucket is required" }} + + # S3 access key configured in SeaweedFS s3 identity config accesskey: {{ .Values.registry.s3.accesskey | required "registry.s3.accesskey is required" }} + + # S3 secret key configured in SeaweedFS s3 identity config secretkey: {{ .Values.registry.s3.secretkey | required "registry.s3.secretkey is required" }} + + # AWS region — required by AWS SDK even when not using real AWS + # SeaweedFS ignores this value, any string is accepted region: {{ .Values.registry.s3.region | default "us-east-1" }} + + # Override default AWS S3 endpoint to point at SeaweedFS S3 + # Required when using any S3-compatible service instead of real AWS regionendpoint: {{ $endpoint }} + + # Use HTTP instead of HTTPS when talking to SeaweedFS + # Set to true if SeaweedFS is configured with TLS secure: {{ .Values.registry.s3.secure | default false }} + + # Use AWS Signature Version 4 to sign all S3 requests + # SeaweedFS requires v4 — setting this to false will cause auth errors v4auth: {{ .Values.registry.s3.v4auth | default true }} + + # Root path prefix inside the bucket where all registry files are stored + # All data will be written to: s3://registry//docker/registry/v2/... + # ⚠ Changing this after data exists will make existing images invisible to registry rootdirectory: {{ .Values.registry.s3.rootdirectory | default "/" }} + + # Force path-style S3 URLs instead of virtual-hosted-style + # Virtual-hosted-style (forcepathstyle: false — default): + # http://registry.seaweedfs:8333/docker/registry/v2/... + # ↑ bucket name embedded in hostname → SeaweedFS cannot resolve + # + # Path-style (forcepathstyle: true): + # http://seaweedfs:8333/registry/docker/registry/v2/... + # ↑ bucket name in URL path → SeaweedFS resolves correctly + # + # ⚠ REQUIRED when using regionendpoint with SeaweedFS + # Without this → error: "Copy Source must mention the source bucket and key" forcepathstyle: {{ .Values.registry.s3.forcepathstyle | default true }} + + # Size of each chunk when uploading a blob to S3 via multipart upload + # Default is 10MB → too many PATCH requests → increases chance of session loss + # 128MB → fewer PATCH requests per blob → more stable with SeaweedFS + # Minimum allowed by S3 spec: 5MB + # Formula: blob 256MB / chunksize 128MB = 2 PATCH requests total chunksize: {{ .Values.registry.s3.chunksize | default 134217728 | int64 }} + + # Blobs SMALLER than this value use a single S3 CopyObject call to finalize + # Blobs LARGER than this value use S3 Multipart Copy (multiple CopyObject calls) + # Default is 32MB → almost all blobs trigger Multipart Copy → hits SeaweedFS CopyObject bug + # 1GB → most image layers (which are typically < 1GB) use single copy → avoids the bug + # Error prevented: "NoSuchUpload" and "Copy Source must mention source bucket and key" multipartcopythresholdsize: {{ .Values.registry.s3.multipartcopythresholdsize | default 1073741824 | int64 }} + + # Size of each part when performing a Multipart Copy on SeaweedFS + # Only applies to blobs larger than multipartcopythresholdsize (> 1GB) + # Set equal to chunksize for consistency multipartcopyChunksize: {{ .Values.registry.s3.multipartcopyChunksize | default 134217728 | int64 }} + + # Maximum number of concurrent Multipart Copy operations + # Default is 100 → 100 parallel CopyObject calls → SeaweedFS race condition + # 1 → serial execution → slower but stable with SeaweedFS + # Only affects blobs larger than multipartcopythresholdsize (> 1GB) multipartcopyMaxConcurrency: {{ .Values.registry.s3.multipartcopyMaxConcurrency | default 1 | int }} + + # Disable S3 pre-signed URL redirect for blob downloads + # When false (redirect enabled): registry returns a pre-signed S3 URL + # → client downloads blob directly from SeaweedFS + # → works only if the client can reach the S3 endpoint + # When true (redirect disabled): registry proxies the blob content itself + # → required when SeaweedFS is only accessible inside the cluster redirect: disable: {{ .Values.registry.storage.redirect.disable | default false }} http: + # Address and port the registry listens on + # ":5000" = 0.0.0.0:5000 → all network interfaces + # Use "127.0.0.1:5000" to restrict to localhost only addr: {{ .Values.registry.http.addr | default ":5000" }} + + # Secret key used to encrypt and decrypt upload session state (_state parameter) + # The _state token encodes: { UUID, Offset, StartedAt } for each active upload + # ⚠ Must be identical across all registry replicas in HA setup + # → if replicas have different secrets, PATCH/PUT from one replica cannot decrypt _state created by another → upload fails + # ⚠ Do not change while uploads are in progress + # → existing _state tokens cannot be decrypted → active uploads fail secret: {{ .Values.registry.http.secret | required "registry.http.secret is required" }} + headers: X-Content-Type-Options: {{ .Values.registry.http.headers.XContentTypeOptions | default "[nosniff]" }} diff --git a/kubernetes/docker-registry/templates/service.yaml b/kubernetes/docker-registry/templates/service.yaml index 8a629474..592ce206 100644 --- a/kubernetes/docker-registry/templates/service.yaml +++ b/kubernetes/docker-registry/templates/service.yaml @@ -5,7 +5,9 @@ metadata: name: {{ include "common.names.fullname" . }}-service namespace: {{ .Release.Namespace }} spec: - selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: docker-registry ports: - name: http port: 5000 diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index 966fdeed..c12ba581 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -102,11 +102,11 @@ createBucket: resources: requests: - cpu: 50m - memory: 64Mi + cpu: 100m + memory: 256Mi limits: - cpu: 200m - memory: 128Mi + cpu: 500m + memory: 1Gi # --------------------------------------------------------------------------- # SeaweedFS (reference) From f09fac1f5fc41b557adce28d01d5d02b86460182 Mon Sep 17 00:00:00 2001 From: kiennkt Date: Wed, 6 May 2026 16:15:14 +0700 Subject: [PATCH 12/12] fix: change image tag --- kubernetes/docker-registry/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes/docker-registry/values.yaml b/kubernetes/docker-registry/values.yaml index c12ba581..c42deef7 100644 --- a/kubernetes/docker-registry/values.yaml +++ b/kubernetes/docker-registry/values.yaml @@ -10,7 +10,7 @@ imagePullSecret: image: registry: ghcr.io repository: svtechnmaa/registry - tag: 2.6 + tag: 2.8 pullPolicy: IfNotPresent # ---------------------------------------------------------------------------