Self-hosted Docker registry в Kubernetes: настройка, аутентификация и очистка
Published: 2026-03-10
Self-hosted registry даёт полный контроль над доступностью образов и устраняет ограничения pull-rate DockerHub. Мы запускаем официальный registry:2 на голом NodePort — просто, без внешних зависимостей, работает в air-gapped установках.
Почему self-hosted
- Air-gapped среды — production-ВМ без интернета тянут только из внутреннего реестра
- Ограничения pull rate — DockerHub ограничивает анонимные pull до 100/6ч на IP
- Задержка — .NET-образ на 500 МБ из DockerHub тянется минуты, из локального реестра — секунды
- Аудит — вы сами решаете, какие образы доступны; никаких неожиданных удалений
- Стоимость — нет оплаты за пользователей или объём хранения
Развёртывание
yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: registry
namespace: registry
spec:
replicas: 1
selector:
matchLabels:
app: registry
template:
metadata:
labels:
app: registry
spec:
containers:
- name: registry
image: registry:2
ports:
- containerPort: 5000
env:
- name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
value: /var/lib/registry
- name: REGISTRY_HTTP_SECRET
valueFrom:
secretKeyRef:
name: registry-secret
key: http-secret
- name: REGISTRY_STORAGE_DELETE_ENABLED
value: "true"
volumeMounts:
- name: registry-data
mountPath: /var/lib/registry
resources:
requests:
cpu: 100m
memory: 128Mi
volumes:
- name: registry-data
persistentVolumeClaim:
claimName: registry-pvc
---
apiVersion: v1
kind: Service
metadata:
name: registry
namespace: registry
spec:
type: NodePort
selector:
app: registry
ports:
- port: 5000
targetPort: 5000
nodePort: 30500
NodePort 30500 доступен с любой машины, которая может достучаться до ноды. REGISTRY_STORAGE_DELETE_ENABLED: "true" включает API удаления манифестов — необходимо для работы garbage collection.
Постоянное хранилище
yamlapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: registry-pvc
namespace: registry
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: local-path
Для продакшна можно использовать S3-совместимое хранилище:
yamlenv:
- name: REGISTRY_STORAGE
value: s3
- name: REGISTRY_STORAGE_S3_BUCKET
value: my-registry-bucket
Настройка Docker для HTTP-реестра
Docker отклоняет HTTP-реестры по умолчанию:
json// /etc/docker/daemon.json
{
"insecure-registries": ["192.168.1.10:30500"]
}
bashsudo systemctl restart docker
Push образов
bashdocker tag myapp:latest 192.168.1.10:30500/myapp:latest
docker push 192.168.1.10:30500/myapp:latest
# Проверить теги
curl -s http://192.168.1.10:30500/v2/myapp/tags/list | jq .
Pull из реестра в Kubernetes-подах
yamlcontainers:
- name: app
image: 192.168.1.10:30500/myapp:abc1234
imagePullPolicy: Always
Если нода обращается к реестру без аутентификации, imagePullSecret не нужен. Для защищённых реестров:
bashkubectl create secret docker-registry registry-creds \
--docker-server=192.168.1.10:30500 \
--docker-username=robot \
--docker-password=TOKEN \
--namespace=app
yamlspec:
imagePullSecrets:
- name: registry-creds
Garbage collection
API реестра не удаляет блобы физически при удалении тега. Запускайте GC периодически:
bashkubectl exec -n registry deploy/registry -- \
registry garbage-collect \
--delete-untagged \
/etc/docker/registry/config.yml
CronJob для автоматизации:
yamlapiVersion: batch/v1
kind: CronJob
metadata:
name: registry-gc
namespace: registry
spec:
schedule: "0 3 * * 0" # каждое воскресенье в 3:00
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: gc
image: registry:2
command:
- registry
- garbage-collect
- --delete-untagged
- /etc/docker/registry/config.yml
volumeMounts:
- name: registry-data
mountPath: /var/lib/registry
volumes:
- name: registry-data
persistentVolumeClaim:
claimName: registry-pvc
Удаление старых тегов через API
bashDIGEST=$(curl -sI \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
"http://192.168.1.10:30500/v2/myapp/manifests/old-tag" \
| grep -i Docker-Content-Digest | awk '{print $2}' | tr -d '\r')
curl -X DELETE "http://192.168.1.10:30500/v2/myapp/manifests/${DIGEST}"
После этого запустите GC для физического освобождения места на диске.
Pull-through кэш для DockerHub
yamlenv:
- name: REGISTRY_PROXY_REMOTEURL
value: "https://registry-1.docker.io"
- name: REGISTRY_PROXY_USERNAME
value: "${DOCKERHUB_USER}"
- name: REGISTRY_PROXY_PASSWORD
value: "${DOCKERHUB_TOKEN}"
Настройте Docker-ноды так, чтобы они тянули образы через 192.168.1.10:30500: после первого pull образ кэшируется, последующие запросы выполняются быстро и не учитываются в лимитах DockerHub.