Özel Depo Kurulumu: Docker Registry Nasıl Kurulur

Ü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.

Yorum yapın