Docker Hub üzerinde ücretsiz hesapla çalışırken bir gün mutlaka duvara toslarsınız: ya anonim çekme limiti dolar ve dağıtım hattınız toomanyrequests hatasıyla durur, ya da özel imajınızı ücretsiz planın izin verdiğinden fazla depoda tutmanız gerekir. Buna bir de kurumsal tarafın klasik sorusu eklenir: müşteri verisiyle çalışan bir uygulamanın imajı gerçekten üçüncü taraf bir bulutta mı durmalı? Kendi Docker registry sunucunuz, bu üç sorunun da tek seferde cevabıdır.
İyi haber şu ki temel bir registry kurmak düşündüğünüzden çok daha basittir; Docker'ın resmi registry:2 imajı tek komutla ayağa kalkar. Zor olan kısmı, onu üretimde güvenle çalıştırmaktır: TLS sertifikası, kimlik doğrulama, disk yönetimi ve silinen imajların gerçekten diskten gitmesi. Bu rehberde sıfırdan bir özel registry kuracağız, önüne bir ters proxy koyup sertifika bağlayacağız, htpasswd ile kimlik doğrulama ekleyeceğiz, imaj gönderip çekeceğiz, Registry API ile içeriği sorgulayacağız ve en sinsi konu olan çöp toplamayı ele alacağız. Sonunda da Docker Hub limitini bitiren pull-through cache kurulumunu göstereceğim.
Kendi Registry'nizi Kurmanın Üç Somut Sebebi#
Birincisi hız ve bant genişliği. Aynı veri merkezinde duran bir registry'den 400 MB'lık imaj çekmek saniyeler sürer; okyanus ötesindeki bir bulut deposundan çekmek dakikalar. On sunuculu bir dağıtımda bu fark, dağıtım süresini doğrudan belirler.
İkincisi bağımsızlık. Docker Hub'ın çekme limitleri anonim ve ücretsiz kullanıcılar için sıkıdır ve CI hattınız aynı genel IP'den çıkıyorsa limit hızla dolar. Kendi registry'niz ya da bir pull-through cache, bu bağımlılığı ortadan kaldırır. Limitle karşılaştıysanız Docker Hub pull limiti hatası yazısında geçici çözümleri de bulabilirsiniz.
Üçüncüsü mahremiyet ve uyum. İmajınızın içinde iş mantığınız, bağımlılık listeniz ve bazen istemeden gömülmüş yapılandırma dosyalarınız vardır. Bunların şirket sınırları içinde kalmasını istemek makul bir gerekliliktir.
Buna karşılık kendi registry'niz size iki yeni sorumluluk yükler: yedekleme ve disk yönetimi. İmaj deposu şaşırtıcı hızda büyür ve kimse fark etmeden diski doldurur. Bunu aşağıda ayrıntısıyla ele alacağım.
Hızlı Başlangıç: registry:2 Konteyneri#
Resmi imaj tek komutla çalışır. Önce yerelde deneyip mekanizmayı görelim, sonra üretim yapılandırmasına geçelim:
# Verinin kalici olmasi icin adlandirilmis hacim kullan
docker volume create registry-data
docker run -d \
--name registry \
--restart unless-stopped \
-p 127.0.0.1:5000:5000 \
-v registry-data:/var/lib/registry \
registry:2
# Calisiyor mu? Bos bir katalog donmeli
curl -s http://127.0.0.1:5000/v2/_catalog
# {"repositories":[]}
Port yayınını 127.0.0.1:5000:5000 biçiminde yazdığıma dikkat edin. Registry'yi doğrudan internete açmak istemiyoruz; dışarıya bakan kısım TLS sonlandıran ters proxy olacak. registry:2 verilerini konteyner içinde /var/lib/registry altında tutar ve bu yolu adlandırılmış bir hacme bağlamak zorunludur; aksi hâlde konteyneri sildiğiniz gün tüm imajlarınız gider.
Üretim için Compose dosyası daha yönetilebilirdir:
services:
registry:
image: registry:2
restart: unless-stopped
ports:
- "127.0.0.1:5000:5000"
environment:
REGISTRY_STORAGE_DELETE_ENABLED: "true" # silmeye izin ver
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: "Firmaniz Registry"
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
volumes:
- registry-data:/var/lib/registry
- ./auth:/auth:ro
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5000/v2/"]
interval: 30s
timeout: 5s
retries: 3
volumes:
registry-data:
REGISTRY_STORAGE_DELETE_ENABLED ayarını en baştan açmanızı öneririm; kapalıyken imaj silme isteği reddedilir ve sonradan açmak için konteyneri yeniden oluşturmanız gerekir.
TLS: Ters Proxy ile Doğru Kurulum#
Docker istemcisi, localhost dışındaki registry'lere yalnızca HTTPS üzerinden bağlanır. TLS olmadan çalışmanın tek yolu her istemcide insecure-registries ayarı yapmaktır; bunu üretimde yapmayın, çünkü kimlik doğrulama bilgileriniz açık ağdan geçer.
En temiz yöntem registry'yi olduğu gibi bırakıp önüne Let's Encrypt sertifikalı bir nginx koymaktır:
server {
listen 443 ssl;
http2 on;
server_name registry.firmaniz.com;
ssl_certificate /etc/letsencrypt/live/registry.firmaniz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/registry.firmaniz.com/privkey.pem;
# Imaj katmanlari buyuk olabilir - varsayilan 1m sinirini kaldir
client_max_body_size 0;
# Buyuk katman aktarimlarinda zaman asimini uzat
proxy_read_timeout 900s;
location /v2/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Registry'nin kendi Docker-Distribution-Api-Version basligi korunmali
proxy_buffering off;
}
}
İki satır özellikle kritiktir. client_max_body_size 0; yazmazsanız 1 MB'ı aşan her katman 413 Request Entity Too Large ile reddedilir ve docker push yarıda kalır. proxy_read_timeout değeri düşükse büyük katmanların aktarımı zaman aşımına uğrar. Bu ikisi, ters proxy arkasındaki registry'de en sık karşılaşılan iki hatadır.
Alan adını yönlendirdikten sonra sertifikayı almak tek komuttur:
sudo certbot --nginx -d registry.firmaniz.com
Kimlik Doğrulama: htpasswd#
TLS trafiği şifreler ama kimin gönderebileceğini belirlemez. Temel registry için en pratik yöntem htpasswd tabanlı temel kimlik doğrulamadır. Registry yalnızca bcrypt ile üretilmiş parola karmalarını kabul eder:
mkdir -p /srv/registry/auth
# bcrypt (-B) zorunlu; -b parolayi komut satirindan, -n cikti dosyaya
docker run --rm --entrypoint htpasswd httpd:2 -Bbn dagitim 'guclu-bir-parola' \
> /srv/registry/auth/htpasswd
chmod 600 /srv/registry/auth/htpasswd
# Ikinci kullanici eklemek icin ciktiyi ekleyerek yazin
docker run --rm --entrypoint htpasswd httpd:2 -Bbn okuyucu 'baska-parola' \
>> /srv/registry/auth/htpasswd
Parolayı komut satırına yazdığınız için kabuk geçmişinize düşer; komutun başına bir boşluk koymak ya da işlem sonrası history -d ile satırı silmek iyi bir alışkanlıktır. Parolayı bir dosyada tutup oradan beslemek daha da temizdir; gizli bilgi yönetiminin genel prensipleri için Docker'da gizli bilgi yönetimi yazısına bakabilirsiniz.
Temel kimlik doğrulamanın önemli bir sınırı vardır: rol ayrımı yoktur. Giriş yapabilen herkes hem çekebilir hem gönderebilir. Ekipler, projeler ve salt okunur robot hesapları gerekiyorsa Harbor ile özel registry kurulumuna geçmeniz gerekir.
İmaj Etiketleme, Gönderme ve Çekme#
Docker'da bir imajın hangi registry'ye ait olduğu, etiketinin ilk bileşeninden anlaşılır. Etikette nokta içeren bir ana makine adı varsa Docker onu registry adresi kabul eder; yoksa Docker Hub varsayar.
# Giris yap - kimlik bilgileri ~/.docker/config.json icine yazilir
docker login registry.firmaniz.com
# Yerel imaji registry adresiyle yeniden etiketle
docker tag firmaniz/web:1.4.0 registry.firmaniz.com/firmaniz/web:1.4.0
# Gonder
docker push registry.firmaniz.com/firmaniz/web:1.4.0
# Baska bir sunucudan cek
docker login registry.firmaniz.com
docker pull registry.firmaniz.com/firmaniz/web:1.4.0
Sunucularda docker login yapmak kimlik bilgilerini ~/.docker/config.json içine base64 kodlu (şifrelenmemiş) olarak yazar. Bu dosyanın izinlerini 600 tutun ve dağıtım için ayrı, yalnızca çekme yetkisi olan bir hesap kullanmayı düşünün.
Compose dosyanızda artık imajı kendi registry'nizden gösterirsiniz:
services:
app:
image: registry.firmaniz.com/firmaniz/web:1.4.0
restart: unless-stopped
latest etiketinden uzak durmanızı özellikle öneririm. Sürüm etiketi kullanmak, hangi sunucuda hangi kodun çalıştığını kesin olarak bilmenizi sağlar; latest ile bunu yalnızca tahmin edebilirsiniz.
Registry API ile İçeriği Sorgulama#
Registry, HTTP API'sini /v2/ altında sunar ve curl ile her şeyi inceleyebilirsiniz. Depo listesini, etiketleri ve bir etiketin sindirim (digest) değerini almak şöyledir:
KIMLIK='dagitim:guclu-bir-parola'
# Tum depolar
curl -su "$KIMLIK" https://registry.firmaniz.com/v2/_catalog
# Bir deponun etiketleri
curl -su "$KIMLIK" https://registry.firmaniz.com/v2/firmaniz/web/tags/list
# Bir etiketin digest degeri - silme icin gerekli
curl -sI -u "$KIMLIK" \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://registry.firmaniz.com/v2/firmaniz/web/manifests/1.4.0 \
| grep -i docker-content-digest
Son komutta Accept başlığını göndermek zorunludur; göndermezseniz registry farklı bir manifest biçimi döner ve aldığınız digest silme isteğinde çalışmaz. Bu, API ile uğraşanların en çok takıldığı ayrıntıdır.
Silme ve Çöp Toplama#
Burası kendi registry'sini kuranların neredeyse tamamının bir gün diski dolduğunda öğrendiği konudur. Bir etiketi silmek diskte hiçbir yer boşaltmaz; yalnızca manifest'i işaretlenmemiş hâle getirir. Gerçek alan, ayrı bir çöp toplama işlemiyle geri alınır.
Sıralama şudur:
- Silinecek etiketin digest değerini yukarıdaki
curlile alın. - Manifest'i digest üzerinden silin.
- Registry'yi salt okunur moda alın ya da yazma trafiğini durdurun.
- Çöp toplayıcıyı çalıştırın.
DIGEST='sha256:8f2c...' # onceki adimdan alinan deger
# 2) Manifest'i sil
curl -X DELETE -u "$KIMLIK" \
https://registry.firmaniz.com/v2/firmaniz/web/manifests/$DIGEST
# 4) Cop toplama - yapilandirma yolu surume gore degisebilir
docker exec registry sh -c 'ls /etc/docker/registry/config.yml /etc/distribution/config.yml 2>/dev/null'
docker exec registry bin/registry garbage-collect --delete-untagged \
/etc/docker/registry/config.yml
Üçüncü adımı atlamayın. Çöp toplama sırasında registry yazma alırsa, tam o anda yüklenmekte olan bir katman "başvurusuz" görünüp silinebilir ve ortaya bozuk bir imaj çıkar. Pratikte en güvenli yol, çöp toplamayı düşük trafikli bir saatte konteyneri durdurup çalıştırmak ya da REGISTRY_STORAGE_MAINTENANCE_READONLY ile salt okunur moda alarak yapmaktır.
Disk kullanımını izlemeyi de rutininize alın:
docker system df -v | grep registry-data
docker exec registry du -sh /var/lib/registry
Docker Hub Limiti için Pull-Through Cache#
Aynı registry:2 imajı, tek bir ortam değişkeniyle önbellek aynasına dönüşür. Bu modda registry'ye gelen istekleri Docker Hub'dan çeker, yerelde saklar ve ikinci kez aynı imaj istendiğinde dışarı hiç çıkmaz. CI hattınızın limit hatası almasını bitiren en pratik çözümdür.
services:
hub-cache:
image: registry:2
restart: unless-stopped
ports:
- "127.0.0.1:5001:5000"
environment:
REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io
# Ozel Hub imajlari icin kimlik bilgisi (istege bagli)
# REGISTRY_PROXY_USERNAME: hub_kullanici
# REGISTRY_PROXY_PASSWORD: hub_token
volumes:
- hub-cache-data:/var/lib/registry
volumes:
hub-cache-data:
Sunucularınıza bu aynayı kullanmalarını daemon.json ile söylersiniz:
{
"registry-mirrors": ["https://cache.firmaniz.com"]
}
Ayarı yazdıktan sonra sudo systemctl restart docker gerekir. Bir aynanın yalnızca çekme için çalıştığını unutmayın; kendi imajlarınızı buraya gönderemezsiniz, onun için ayrı bir registry örneği kullanın.
Sık Yapılan Hatalar#
Veriyi hacme bağlamamak. -v olmadan çalıştırılan registry, konteyner silindiğinde tüm imajları da götürür. Adlandırılmış hacim kullanın ve o hacmi yedekleme kapsamınıza alın.
insecure-registries ile idare etmek. Her istemcide ayar gerektirir, unutulan bir sunucuda dağıtım kırılır ve kimlik bilgileriniz şifresiz gider. Ters proxy ile TLS kurmak yarım saatlik bir iştir.
client_max_body_size ayarını unutmak. nginx arkasında docker push komutunun büyük katmanlarda 413 ile ölmesinin tek sebebi budur ve hata mesajı Docker tarafında anlaşılmaz görünür.
Çöp toplamayı hiç çalıştırmamak. Silinen etiketler diski boşaltmaz. Aylar sonra "registry diski doldu" diye bakınca, çoğu artık kimsenin kullanmadığı katmanlar olduğunu görürsünüz. Aylık bir bakım işi planlayın.
htpasswd dosyasını bcrypt olmadan üretmek. Registry yalnızca bcrypt karmalarını kabul eder; -B bayrağını unutursanız her giriş denemesi 401 döner ve sebebi loglardan kolay anlaşılmaz.
Yedeklemeyi atlamak. Registry hacmi kaybolursa tüm sürüm geçmişiniz gider. En azından registry-data hacminin düzenli kopyasını alın; Docker hacimlerini yedekleme yöntemleri için Docker uygulama yedekleme ve geri yükleme yazısına bakın.
Sıkça Sorulan Sorular#
Kendi Docker registry kurmak ücretsiz mi#
Yazılım tarafı tamamen ücretsizdir; registry:2 açık kaynaklıdır ve lisans bedeli yoktur. Maliyet sunucu kaynaklarındadır: imajları saklayacak disk, çekme trafiği için bant genişliği ve TLS için bir alan adı. Küçük bir ekip için mütevazı bir sanal sunucu fazlasıyla yeterlidir; asıl planlamanız gereken şey diskin zamanla nasıl büyüyeceğidir.
Registry ne kadar disk alanı ister#
Kabaca hesap şöyledir: imajınızın sıkıştırılmış boyutu çarpı saklamak istediğiniz sürüm sayısı, artı ortak katmanların bir kez sayılmasından gelen indirim. Katmanlar depolar arasında paylaşıldığı için aynı temel imajı kullanan yirmi uygulama bunu tek kez saklar. Yine de eski sürümleri hiç temizlemezseniz büyüme doğrusal devam eder; imaj boyutunu küçültmek için Docker imaj boyutu optimizasyonu yazısındaki yöntemler doğrudan disk tasarrufuna dönüşür.
İmajı sildim ama disk boşalmadı, neden#
Çünkü etiket silme işlemi yalnızca manifest başvurusunu kaldırır; katmanlar diskte durmaya devam eder. Alanı geri almak için registry garbage-collect komutunu --delete-untagged seçeneğiyle çalıştırmanız gerekir. Ayrıca silmenin çalışabilmesi için registry'nin REGISTRY_STORAGE_DELETE_ENABLED=true ile başlatılmış olması şarttır; kapalıysa silme isteği doğrudan reddedilir.
Registry'ye kullanıcı bazlı yetki verebilir miyim#
Temel registry:2 kurulumunda hayır; htpasswd ile kimlik doğrulayan herkes hem çeker hem gönderir. Proje bazlı yetkiler, salt okunur robot hesapları, LDAP entegrasyonu ve denetim kaydı istiyorsanız Harbor gibi bir üst katman gerekir. Ara bir çözüm olarak ters proxy düzeyinde yol bazlı kısıtlama yazabilirsiniz ama bu kırılgandır ve bakımı zordur.
Pull-through cache ile normal registry aynı anda çalışır mı#
Aynı registry örneği ikisini birden yapamaz: bir örnek ya ayna modundadır ya da normal depodur. Ancak aynı sunucuda iki ayrı konteyner çalıştırabilirsiniz; biri 5000 portunda kendi imajlarınız için, diğeri 5001 portunda Docker Hub aynası olarak. Sunuculara aynayı registry-mirrors ile, kendi registry'nizi ise imaj etiketiyle tanıtırsınız.
Registry'yi nasıl yedeklerim#
Yedeklenmesi gereken tek şey depolama hacmidir; registry-data hacminin içeriğini düzenli olarak kopyalayın. En güvenli yöntem registry'yi kısa süreliğine durdurup hacmi arşivlemek ya da salt okunur moda alıp kopyalamaktır, çünkü yükleme sırasında alınan bir kopya yarım katman içerebilir. htpasswd dosyasını ve TLS sertifikalarını da aynı yedek kapsamına almayı unutmayın.
Docker push neden 413 hatası veriyor#
Bu hata registry'den değil, önündeki ters proxy'den gelir. nginx varsayılan olarak istek gövdesini 1 MB ile sınırlar ve imaj katmanları bunun çok üzerindedir. Sunucu bloğuna client_max_body_size 0; satırını ekleyip nginx'i yeniden yükleyin. Aynı senaryoda büyük katmanlarda zaman aşımı yaşarsanız proxy_read_timeout değerini de yükseltmeniz gerekir.
Kapanış#
Kendi registry'nizi kurmak yarım günlük bir iştir ama uzun vadede dağıtım hızınızı, bağımsızlığınızı ve gizliliğinizi belirgin biçimde iyileştirir. Aklınızda kalması gereken dört alışkanlık şunlar: veriyi mutlaka adlandırılmış bir hacimde tutup yedekleyin, TLS'i ters proxy ile kurup client_max_body_size 0 satırını unutmayın, latest yerine sürüm etiketi kullanın ve çöp toplamayı aylık bir bakım işi olarak takviminize yazın. Rol bazlı yetki, zafiyet taraması ve denetim kaydı ihtiyacınız doğduğunda ise temel registry'den Harbor'a geçmenin zamanı gelmiş demektir.
Registry gibi sürekli ayakta kalması gereken bir servis için sağlam bir altyapı seçmek işin yarısıdır. Disk genişletmesi kolay bir bulut sunucu ya da tam root erişimli VDS paketlerimiz bu iş için uygun bir başlangıçtır; sertifika, güncelleme ve izleme tarafını devretmek isterseniz sunucu yönetimi hizmetimiz devreye girer. Registry verinizin düzenli kopyası için yedekleme çözümümüze, alan adı ve sertifika ihtiyacınız için de alan adı ve SSL sayfalarımıza göz atabilirsiniz.