Üretim ortamında Docker image’larını Docker Hub’a push etmek güzel bir çözüm gibi görünse de, kurumsal ortamlarda bu yaklaşım ciddi sorunlar doğurur. Güvenlik politikaları, bant genişliği maliyetleri ve private image’ların dışarıya çıkmaması gereken durumlarda kendi Docker Registry’nizi kurmak hem zorunluluk hem de en doğru tercih haline gelir. Bu yazıda sıfırdan özel bir Docker Registry nasıl kurulur, TLS ile nasıl güvenli hale getirilir ve production ortamına nasıl hazır hale getirilir, adım adım inceleyeceğiz.
Docker Registry Nedir ve Neden Gerekli?
Docker Registry, container image’larını depoladığınız ve dağıttığınız bir sunucudur. Docker Hub da aslında büyük bir public registry’dir. Kendi registry’nizi kurduğunuzda şu avantajları elde edersiniz:
- Ağ hızı: Image’lar iç ağda kaldığı için pull/push işlemleri çok daha hızlı gerçekleşir
- Güvenlik: Hassas uygulama image’larınız asla dış dünyaya çıkmaz
- Maliyet: Docker Hub’ın ücretli planlarına ihtiyaç kalmaz
- Kontrol: Hangi image’ların depolandığını ve kimlerle paylaşıldığını tamamen siz belirlersiniz
- Offline ortam: İnternet erişimi olmayan air-gapped ortamlarda da çalışabilirsiniz
Tipik bir senaryo düşünelim: 50 geliştiricili bir ekip var, her sabah CI/CD pipeline’ından 2-3 GB boyutunda image’lar çekiyor. Bu image’lar Docker Hub üzerinden gelirse hem Docker Hub rate limit’e takılırsınız hem de inanılmaz bir bant genişliği tüketirsiniz. Kendi registry’nizi kurduğunuzda bu problem tamamen ortadan kalkar.
Gereksinimler ve Ortam Hazırlığı
Bu kurulum için şunlara ihtiyacınız var:
- Ubuntu 22.04 veya CentOS/RHEL 8+ çalıştıran bir sunucu (en az 2 CPU, 4 GB RAM)
- Docker Engine kurulu
- Docker Compose v2+
- Bir alan adı veya iç DNS kaydı (TLS için gerekli)
- Açık portlar: 443 (HTTPS) veya 5000 (HTTP, test için)
Önce Docker Engine’in kurulu olduğunu doğrulayın:
docker --version
docker compose version
Çalışma dizinini oluşturun:
mkdir -p /opt/registry/{data,certs,auth}
cd /opt/registry
Basit Bir Registry ile Başlayalım
İlk adımda HTTP üzerinden çalışan basit bir registry kuracağız. Bu yöntem sadece test ve geliştirme ortamları için uygundur, production’da kullanmayın.
docker run -d
-p 5000:5000
--name registry
--restart=always
-v /opt/registry/data:/var/lib/registry
registry:2
Registry’nin çalışıp çalışmadığını test edin:
curl http://localhost:5000/v2/_catalog
Çıktı şu şekilde olmalı:
{"repositories":[]}
Şimdi bir test image’ı push edelim:
docker pull ubuntu:22.04
docker tag ubuntu:22.04 localhost:5000/ubuntu:22.04
docker push localhost:5000/ubuntu:22.04
Catalog endpoint’ini tekrar sorgularsanız image’ın eklendiğini göreceksiniz. Bu noktaya kadar her şey basit. Ama gerçek ortamda ihtiyacınız olan şey TLS, authentication ve yönetilebilir bir yapı.
TLS Sertifikası Oluşturma
Production registry için self-signed sertifika veya Let’s Encrypt kullanabilirsiniz. İç ağ için self-signed sertifika yeterlidir. Önce self-signed sertifika yöntemiyle ilerleyeceğiz.
cd /opt/registry/certs
openssl req -newkey rsa:4096 -nodes -sha256
-keyout domain.key
-x509 -days 365
-out domain.crt
-subj "/CN=registry.company.internal"
-addext "subjectAltName=DNS:registry.company.internal,IP:192.168.1.100"
Burada registry.company.internal yerine kendi alan adınızı veya iç DNS kaydınızı yazın. IP adresi kısmına da sunucunuzun IP’sini girin.
Self-signed sertifikayı Docker daemon’ının güveneceği yere kopyalayın. Bu adım çok kritik, yoksa Docker client’lar bağlanmayı reddeder:
# Ubuntu/Debian için
mkdir -p /etc/docker/certs.d/registry.company.internal:443
cp /opt/registry/certs/domain.crt /etc/docker/certs.d/registry.company.internal:443/ca.crt
# Sistem sertifika deposuna da ekleyin
cp /opt/registry/certs/domain.crt /usr/local/share/ca-certificates/registry.crt
update-ca-certificates
Registry’ye bağlanacak her Docker client makinede bu adımı tekrarlamanız gerekir. Büyük ortamlarda bu işlemi Ansible veya benzeri bir konfigürasyon yönetim aracıyla otomatize etmek hayat kurtarır.
Kimlik Doğrulama Ayarları
Registry’ye herkesin erişmesini istemiyorsak htpasswd ile temel kimlik doğrulaması ekleyebiliriz:
# htpasswd aracını yükleyin
apt install apache2-utils -y
# Kullanıcı oluşturun (ilk kullanıcı için -c flag'i kullanın)
htpasswd -Bc /opt/registry/auth/htpasswd admin
# Parola girmeniz istenecek
# İkinci kullanıcı eklemek için -c olmadan:
htpasswd -B /opt/registry/auth/htpasswd developer
Docker Compose ile Production Kurulumu
Şimdi her şeyi bir araya getiren Docker Compose dosyasını hazırlayalım:
# /opt/registry/docker-compose.yml
version: '3.8'
services:
registry:
image: registry:2
container_name: docker-registry
restart: always
ports:
- "443:5000"
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: "Registry Realm"
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
REGISTRY_HTTP_SECRET: "guclu-bir-secret-buraya-yazin"
REGISTRY_LOG_LEVEL: info
REGISTRY_STORAGE_DELETE_ENABLED: "true"
volumes:
- /opt/registry/data:/data
- /opt/registry/certs:/certs:ro
- /opt/registry/auth:/auth:ro
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "https://localhost:5000/v2/"]
interval: 30s
timeout: 10s
retries: 3
Registry’yi başlatın:
cd /opt/registry
docker compose up -d
docker compose logs -f
Artık HTTPS üzerinden bağlanabilirsiniz:
curl -u admin:parolaniz https://registry.company.internal/v2/_catalog
Client Makinelerde Yapılandırma
Registry sunucunuz hazır, şimdi geliştiricilerin bu registry’yi kullanabilmesi için client makineleri yapılandırmak gerekiyor. Her client makinede şu adımları uygulayın:
# Sertifikayı registry sunucusundan kopyalayın
scp [email protected]:/opt/registry/certs/domain.crt /tmp/
# Docker'ın cert dizinine yerleştirin
mkdir -p /etc/docker/certs.d/registry.company.internal:443
cp /tmp/domain.crt /etc/docker/certs.d/registry.company.internal:443/ca.crt
# Docker daemon'ını yeniden başlatın
systemctl restart docker
Artık geliştiriciler şu şekilde login olabilir:
docker login registry.company.internal
# Kullanıcı adı ve parola soracak
Image push ve pull işlemleri:
# Image tag'leme
docker tag myapp:latest registry.company.internal/myapp:latest
docker tag myapp:latest registry.company.internal/myapp:1.0.0
# Push
docker push registry.company.internal/myapp:latest
# Pull
docker pull registry.company.internal/myapp:latest
Garbage Collection: Disk Temizliği
Registry kullandıkça disk dolmaya başlar. Eski ve kullanılmayan image layer’larını temizlemek için garbage collection çalıştırmanız gerekir. Bu işlemi yaparken dikkatli olun, bazı referansları kırabilirsiniz.
# Önce hangi image'ların silineceğini görmek için dry-run yapın
docker exec docker-registry registry garbage-collect
--dry-run /etc/docker/registry/config.yml
# Gerçekten temizlemek için
docker exec docker-registry registry garbage-collect
/etc/docker/registry/config.yml
Garbage collection sırasında registry’yi read-only moda almanızı öneririm. Aksi takdirde push işlemleri sırasında veri tutarsızlığı oluşabilir. Bunun için registry’yi REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED=true environment variable’ı ile yeniden başlatın, cleanup yapın, sonra tekrar normal moda döndürün.
Bunu bir cron job haline getirmek pratik bir çözümdür:
# /etc/cron.weekly/registry-cleanup
#!/bin/bash
docker exec docker-registry registry garbage-collect
/etc/docker/registry/config.yml >> /var/log/registry-gc.log 2>&1
Nginx ile Reverse Proxy Kurulumu
Birçok kurumda 443 portu zaten Nginx veya Apache tarafından kullanılıyor olabilir. Bu durumda registry’yi Nginx’in arkasına koyabilirsiniz. Ayrıca Nginx ile daha gelişmiş access control ve logging yapabilirsiniz.
Nginx konfigürasyonu:
# /etc/nginx/conf.d/registry.conf
upstream docker-registry {
server 127.0.0.1:5000;
}
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}
server {
listen 443 ssl;
server_name registry.company.internal;
ssl_certificate /opt/registry/certs/domain.crt;
ssl_certificate_key /opt/registry/certs/domain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Docker Registry için önemli: büyük image'lar için boyut limiti
client_max_body_size 0;
chunked_transfer_encoding on;
location /v2/ {
if ($http_user_agent ~ "^(docker/1.(3|4|5(?!.[0-9]-dev))|Go ).*$" ) {
return 404;
}
add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
proxy_pass http://docker-registry;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
server {
listen 80;
server_name registry.company.internal;
return 301 https://$server_name$request_uri;
}
Bu konfigürasyonda client_max_body_size 0 ayarı kritik önem taşır. Nginx varsayılan olarak büyük dosyaları reddeder, bu değeri 0 yaparak sınırsız upload’a izin verirsiniz. Birkaç GB’lık image push etmeye çalışıp “413 Request Entity Too Large” hatası alan sysadminlerin büyük çoğunluğu bu ayarı atladığı için bu hatayı alır.
Registry UI Kurulumu
Komut satırı güzel ama takım arkadaşlarınıza “hangi image’lar var, hangi tag’ler mevcut” gibi soruları kolayca yanıtlayabileceğiniz bir web arayüzü hayatı kolaylaştırır. joxit/docker-registry-ui bu iş için oldukça kullanışlıdır.
Docker Compose dosyanıza UI servisini ekleyin:
# docker-compose.yml'e eklenecek servis
registry-ui:
image: joxit/docker-registry-ui:latest
container_name: registry-ui
restart: always
ports:
- "8080:80"
environment:
REGISTRY_TITLE: "Şirket Docker Registry"
REGISTRY_URL: "https://registry.company.internal"
SINGLE_REGISTRY: "true"
DELETE_IMAGES: "true"
SHOW_CONTENT_DIGEST: "true"
depends_on:
- registry
docker compose up -d registry-ui
Artık http://registry-sunucu:8080 adresinden web arayüzüne erişebilirsiniz. Hangi image’ların mevcut olduğunu, tag’leri ve manifest bilgilerini görsel olarak inceleyebilirsiniz.
Monitoring ve Alerting
Registry’nin sağlığını izlemek için birkaç kritik nokta var. Registry, Prometheus metriklerini destekler. Docker Compose’daki environment variable’lara şunu ekleyin:
REGISTRY_HTTP_DEBUG_ADDR: ":5001"
REGISTRY_HTTP_DEBUG_PROMETHEUS_ENABLED: "true"
Basit bir health check scripti:
#!/bin/bash
# /usr/local/bin/registry-healthcheck.sh
REGISTRY_URL="https://registry.company.internal"
AUTH="admin:parolaniz"
# API v2 endpoint kontrolü
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}"
-u "$AUTH" "$REGISTRY_URL/v2/")
if [ "$HTTP_STATUS" != "200" ]; then
echo "CRITICAL: Registry yanıt vermiyor! HTTP Status: $HTTP_STATUS"
# Slack veya e-posta bildirimi gönderin
exit 2
fi
# Disk kullanımı kontrolü
DISK_USAGE=$(df /opt/registry/data | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 85 ]; then
echo "WARNING: Disk kullanımı %$DISK_USAGE seviyesinde!"
exit 1
fi
echo "OK: Registry çalışıyor, disk kullanımı %$DISK_USAGE"
exit 0
Bu scripti cron’a ekleyin:
chmod +x /usr/local/bin/registry-healthcheck.sh
echo "*/5 * * * * root /usr/local/bin/registry-healthcheck.sh >> /var/log/registry-health.log 2>&1" >> /etc/cron.d/registry-monitor
Yedekleme Stratejisi
Registry verisini yedeklemek için en basit yöntem, data dizinini tar’lamaktır. Ancak production ortamında registry çalışırken yedek almak veri tutarsızlığına yol açabilir.
#!/bin/bash
# /usr/local/bin/registry-backup.sh
BACKUP_DIR="/backup/registry"
REGISTRY_DATA="/opt/registry/data"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/registry-backup-$DATE.tar.gz"
mkdir -p $BACKUP_DIR
echo "[$DATE] Registry yedekleme başlıyor..."
# Registry'yi geçici olarak durdur
docker stop docker-registry
# Yedek al
tar -czf $BACKUP_FILE -C $REGISTRY_DATA .
# Registry'yi yeniden başlat
docker start docker-registry
echo "Yedek alındı: $BACKUP_FILE ($(du -sh $BACKUP_FILE | cut -f1))"
# 30 günden eski yedekleri sil
find $BACKUP_DIR -name "registry-backup-*.tar.gz" -mtime +30 -delete
echo "Eski yedekler temizlendi."
Yedekleme penceresinde downtime yaşamak istemiyorsanız storage backend olarak S3 veya Azure Blob Storage kullanmayı düşünebilirsiniz. Bu durumda cloud provider’ın kendi yedekleme mekanizmalarından faydalanırsınız.
Sık Karşılaşılan Sorunlar ve Çözümleri
“x509: certificate signed by unknown authority” hatası
Bu hata, Docker client’ın registry sertifikasına güvenmediği anlamına gelir. Sertifikayı /etc/docker/certs.d/registry.company.internal:443/ca.crt dizinine kopyaladığınızdan ve Docker daemon’ını yeniden başlattığınızdan emin olun.
“no basic auth credentials” hatası
Registry’ye docker login yapmadan push/pull yapmaya çalışıyorsunuzdur. docker login registry.company.internal komutuyla önce giriş yapın.
Push sırasında timeout veya bağlantı kesilmesi
Büyük image’lar push ederken Nginx’in proxy_read_timeout değerini artırmanız gerekebilir. 900 saniye genellikle yeterli olur, ama çok büyük image’lar için 1800’e çıkarabilirsiniz.
Registry silme işlemi çalışmıyor
REGISTRY_STORAGE_DELETE_ENABLED: "true" environment variable’ının set edildiğinden emin olun. Bu olmadan API üzerinden silme işlemi “405 Method Not Allowed” döner.
CI/CD Pipeline Entegrasyonu
GitLab CI veya Jenkins ile registry entegrasyonu için .gitlab-ci.yml örneği:
stages:
- build
- push
variables:
REGISTRY: registry.company.internal
IMAGE_NAME: $REGISTRY/myapp
build:
stage: build
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
push:
stage: push
script:
- echo "$REGISTRY_PASSWORD" | docker login $REGISTRY -u $REGISTRY_USER --password-stdin
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:latest
only:
- main
CI/CD parolalarını GitLab’ın protected variables özelliğiyle saklayın, asla .gitlab-ci.yml içine yazmayın.
Sonuç
Kendi Docker Registry’nizi kurmak başlangıçta karmaşık görünebilir, ama adımları tek tek uyguladığınızda oldukça yönetilebilir bir yapı ortaya çıkıyor. Özellikle ekip büyüdükçe ve image sayısı arttıkça bu yatırımın geri dönüşünü çok net göreceksiniz.
Özetlemek gerekirse kritik noktalar şunlar:
- TLS olmadan production’a çıkmayın, self-signed bile olsa mutlaka sertifika kullanın
- Authentication zorunlu, htpasswd basit ama etkili bir başlangıç noktasıdır
- Garbage collection ihmal edilirse diskler dolup registry çöker
- Yedekleme stratejisini baştan belirleyin, sonradan hatırlamak geç kalabilir
- Client makinelerde sertifika dağıtımını otomatize edin, elle yapmak ölçeklenmiyor
Daha ileri seviye için Harbor veya Nexus Repository Manager gibi enterprise registry çözümlerine bakabilirsiniz. Bu araçlar role-based access control, vulnerability scanning ve daha gelişmiş replication özellikleri sunar. Ama küçük ve orta ölçekli ekipler için vanilla Docker Registry artı bu yazıda anlattığım konfigürasyonlar fazlasıyla yeterli.