1 puan yazan GN⁺ 2024-07-13 | 1 yorum | WhatsApp'ta paylaş
  • Konteyner imajları genellikle özel registry'lere yüklenir; ancak bir S3 bucket'ını HTTP üzerinden erişilebilir hale getirip dosyaları beklenen yollara yerleştirirseniz, docker pull hedefi gibi kullanılabilir
  • Özel imaj builder'da pull edilebilir imajları birkaç saniye içinde oluşturma süreci darboğaz hâline geldi ve layer push süresi bunun önemli bir bölümünü oluşturdu
  • 198 MiB'lik layer yükleme benchmark'ında ECR 24–28 MiB/s, S3 ise 115–190 MiB/s olarak ölçüldü; S3 en fazla 8 kat daha hızlıydı
  • Fark, S3'ün tek bir layer'ın chunk'larını paralel yükleyebilmesinden, OCI Distribution Spec tabanlı ECR'nin ise chunk'ları sıralı işlemek zorunda olmasından kaynaklanıyor
  • Bu yöntem docker push, imaj doğrulama, güvenlik taraması, private depo erişim kontrolü gibi registry özelliklerinin yerine geçmez; bu yüzden deneysel bir optimizasyon olarak görülmeli

S3 bucket'ından docker pull yapılabilmesinin koşulları

  • S3'ü konteyner registry'si gibi kullanmak için bucket'ı HTTP üzerinden erişilebilir hâle getirmeniz ve imaj dosyalarını Docker'ın beklediği yollara yüklemeniz gerekir
  • Koşullar sağlanırsa imaj, normal bir registry'den olduğu gibi docker pull ile alınabilir
  • Demo imajı cowsay çalıştırır ve bucket URL'si imaj adı gibi kullanılarak docker run --rm .../cowsay ile çalışır
  • Demoda Cloudflare R2 kullanıldı
    • Ücretsiz egress tercih sebebiydi
    • R2 ve S3 API uyumlu olduğundan imaj AWS SDK ile R2'ye yüklendi

Neden özel registry yerine S3 değerlendirildi?

  • Konteyner imajları genellikle DockerHub, GitHub Container Registry, ECR gibi özel registry'lerde barındırılır
  • Özel imaj builder'ın hedefi, gereksinimlerden pull edilebilir imaja birkaç saniye içinde ulaşmaktır
  • AWS ortamında ECR en kolay seçenek olsa da gerçek yükleme hızında S3 ile ECR arasındaki fark belirgin biçimde ortaya çıktı
  • Performans optimizasyonu için koda execution trace eklendiğinde, layer'ları konteyner registry'sine push etme süresinin büyük bir darboğaz olduğu görüldü

198 MiB layer yükleme sonuçları

  • Küçük benchmark, 198 MiB'lik bir layer'ı ECR ve S3'e ayrı ayrı yükleyerek geçen süreyi ve throughput'u karşılaştırdı
  • Gözlemlenen hızlar şöyleydi
    • ECR: minimum 24 MiB/s, 8,2 saniye
    • ECR: maksimum 28 MiB/s, 7,0 saniye
    • S3: minimum 115 MiB/s, 1,7 saniye
    • S3: maksimum 190 MiB/s, 1,0 saniye
  • Sonuçlara göre S3, ECR'den en fazla 8 kat daha hızlı seviyede
  • Deney kodu AWS üzerinde çalıştırıldı; S3 ve ECR, public internete çıkmadan VPC üzerinden dahili olarak bağlandı
    • Bu koşul, mümkün olan iyi gecikme süresi ve bant genişliğini sağlıyor

Hız farkını yaratan paralel chunk yükleme

  • S3 yüklemesi, tek bir layer'ın chunk'larını paralel olarak yükleyebilir
  • Yeterli bant genişliği varsa paralel chunk yükleme throughput'u ciddi ölçüde artırır
  • AWS belgeleri de bant genişliği kullanımını maksimize etmek için paralel chunk yüklemeyi önerir
  • ECR, OCI Distribution Spec'i uygular
    • Bu spesifikasyon, docker pull ve docker push komutlarının birden fazla registry'de çalışmasını sağlayan standarttır
    • Layer push işlemi sıralı ilerlemek zorundadır; chunk yükleme yapılsa bile önceki chunk bitmeden sonraki chunk'a geçilemez
  • S3'te de sıralı yükleme test edildiğinde throughput ECR ile benzer seviyeye düşer

docker pullun gerçek istek yapısı

  • docker pullun dahili istekleri birden çok HEAD ve GET isteğinden oluşur
  • Örnek akış şöyledir
    • İmaj manifest'inin varlığını kontrol etme: HEAD /v2/my-image/manifests/latest
    • İmaj manifest'ini indirme: GET /v2/my-image/manifests/latest
    • Manifest hash'iyle yeniden indirme: GET /v2/my-image/manifests/sha256:...
    • İmaj metadata blob'unu indirme: GET /v2/my-image/blobs/sha256:...
    • İmaj layer blob'unu indirme: GET /v2/my-image/blobs/sha256:...
  • Sonuçta docker pull, gerekli dosyaları HTTP üzerinden indirme sürecine oldukça yakındır
  • Statik dosya sunucusu gerekli dosyaları beklenen yollara koyar ve her isteğe uygun Content-Type header'ını ayarlarsa konteyner imajı pull edilebilir
  • S3 bucket'ı bu iki koşulu karşılayabildiği için dikkatli yapılandırıldığında konteyner registry'si gibi davranır

Deneysel yaklaşımın sınırları

  • Bu yöntem hâlâ deneysel ve daha fazla araştırma yapılmadan güçlü sonuçlara varmak zor
  • S3, kesin anlamda bir konteyner registry'si değildir
    • docker push yapılamaz
    • docker pull yapılabilmesi, HTTP istek yapısı ile statik dosya sunma biçiminin örtüşmesinin sonucudur
  • Mevcut konteyner registry'leri, dosyaları bir bucket'a yükleme yönteminden daha fazla özellik sunar
    • Standart push yöntemiyle yüklenen imajın gerçekten geçerli olduğuna güvenilebilir
    • Layer'lar için otomatik güvenlik taraması ve uyarılar sunabilir
    • Private depo erişim izinleri native olarak tanımlanabilir
  • Beklendiği gibi çalışırsa public konteyner imajlarını Cloudflare R2'de barındırma yöntemi de mümkün hâle gelebilir

1 yorum

 
GN⁺ 2024-07-13
Hacker News yorumları
  • OCI Distribution Spec iyi tasarlanmış bir şartname gibi okunmadığı için üzücü
    Şartnameye göre katman push işlemleri sıralı yapılmak zorunda; bu yüzden chunk olarak yükleseniz bile her chunk bitmeden sonrakine geçilemiyor. DockerHub ve GHCR üzerinde yaptığım testlere göre chunk upload zaten bozuk ve istemciler de her blob/katmanı tek parça olarak yükleme eğiliminde. Şartname ayrıca RFC7233 biçimiyle uyumlu olmayan bir Content-Range değer biçimini de öneriyor
    Elbette blob düzeyinde paralellik var, ama blob içi paralellik yok. Bir diğer şikâyetim de tag listesinin sayfalandırmasını standartlaştırma fırsatının kaçırılmış olması. Standarttaki ilgili ifade yanlışlıkla silinince [1], her registry bunu kendi kafasına göre uygulamaya başladı
    [1] https://github.com/opencontainers/distribution-spec/issues/4...

    • Docker ve container çevresindeki teknolojiler genel olarak böyle. Kullanıcı deneyimi olarak Docker harika, ama teknoloji olarak neredeyse karmakarışık
      Bunu tamamen kötülemek için söylemiyorum. Gerçekten devrim niteliğindeydi; Linux namespace kullanımını eskisine göre çok daha kolay hâle getirdi ve dünyayı daha iyi bir yöne değiştirdi. Sadece teknik olgunluktan ziyade her zaman kullanıcı deneyimini öne koydu; bu da başlı başına kötü bir şey değil. Perl ya da FTP ile gönderilip alınan CSV'lerle pahalı sorunları çözen pek çok sıkıcı şirket olduğu gibi, sıkıcı hatta kötü teknolojiler bile iyi bir paketle sunulduğunda büyük değer yaratabiliyor
      Yine de bazen bugünkünden çok daha iyi olabilirdi diye düşününce burukluk hissediyorum
    • Buna ek olarak, sorun OCI şartnamesinde mi yoksa AWS mi tuhaf davranıyor bilmiyorum ama GitLab veya Nexus'un aksine AWS ECR klasörlerin otomatik oluşturulmasını desteklemiyor
      Örneğin .dkr.ecr..amazonaws.com/foo/bar/baz:tag gibi bir biçim çalışmıyor; yalnızca düz depolama mümkün olduğu için image adları veya tag'ler gereğinden fazla uzuyor. Teoride Terraform'da ECR repository nesnesi oluşturarak benzer bir şeyi taklit edebilirsiniz, ama sonuç image yolu dinamik olan pipeline'larda pek iyi değil. CI pipeline'ının IAM rolüne rahatsız edici derecede fazla yetki vermek gerekiyor ve AWS kaynaklarının merkezi Terraform deposunun dışında yönetilmesi de hoşuma gitmiyor
      [1] https://stackoverflow.com/questions/64232268/storing-images-...
  • Cloudflare bir zamanlar R2 kullanan container registry server'ını açık kaynak olarak yayımlamıştı
    Kullanan var mı merak ediyorum
    [1]: https://github.com/cloudflare/serverless-registry

    • İyi görünüyor. Ancak katman başına 500MB sınırı olduğu yazıyor
      Bazı kullanım alanlarında büyük sorun olmayabilir, ama bazı durumlarda doğrudan elenme sebebi olabilir
  • Yazarıyım. OCI spesifikasyonunda katman push işleminin neden sıralı olması gerektiğini bilen varsa anlatırsa sevinirim
    Bunun basitçe tarihsel bir tesadüf mü, yoksa gizli bir nedeni mi var merak ediyorum. Açık söylemek gerekirse, birden çok katman elbette paralel push edilebilir; burada bahsettiğim, tek bir katman içeriğinin sıralı push edilmesi gereken kısım

    • Temizliği basitleştirdiği için olabilir. “Son” chunk’a ulaşılmadıysa, N+Timeout sonrasında bunun tamamlanmamış bir upload olduğu açıktır; dolayısıyla silinmesi yeterlidir
      Kısmi upload’ların nasıl ele alınacağına dair uygulama ayrıntısını basitleştirmiş olur. Aksi halde her chunk’ın sonunda diğer tüm chunk’ların mevcut olup olmadığını kontrol edip tamamlama işlemi yapmak gerekir. Yine de bu bir uygulama ayrıntısı ve anlamlı ya da bilinçli bir tasarım olup olmadığı şüpheli. S3 yaklaşımı iyi çalışacak gibi görünüyor; geçmişte büyük imajlar dağıtan bir şirkette benzer bir şeyi denemiştim. Aylık GB başına 0,10 dolar epey birikiyordu
      ECR’nin ek özelliklerini kaybediyorsunuz ama şahsen bu özelliklerin oldukça sınırlı olduğunu düşünüyorum
    • Push tarafıyla hiç uğraşmadım ama bu yaklaşımı görmek sevindirici. Docker’ın ilk dönemlerinde kullanılabilir bir özel registry konteyneri yoktu; imajları nginx’in arkasına koyup pull ediyorduk, bu yüzden yazıyı keyifle okudum
    • OCI uyumlu bir registry [1] uyguladım; spesifikasyon karmaşık olduğu için çoğunlukla spesifikasyondan ziyade referans uygulamanın [2] davranışını takip ettik
      İstemci blob upload’ını bitirirken tüm blob’un digest’ini sağlamalı. Bu gereksinim, sunucunun aldığı baytların bütünlüğünü doğrulayabilmesi için var gibi görünüyor. Sunucu digest kontrolüne ancak son HTTP isteğinde başlarsa, önceki HTTP isteklerinde depoya yazılmış blob içeriğinin tamamını tekrar okuması gerekir. Büyük katmanlarda bu gecikme kabul edilemeyebilir. Belirli bir istemci gereksinimi nedeniyle 150GiB blob’lara kadar çalıştığını doğruladık
      Bunun yerine bizim uygulamada digest hesaplamasını tüm istek dizisi boyunca sürdürürüz. Blob verisini chunk’lar halinde alırken aynı anda digest hesaplaması yapar ve blob deposuna stream ederiz. Her istek arasında digest hesaplama durumunu, Location header’ıyla istemciye döndürülen upload URL’sinin içinde serileştiririz. Kabaca şu kodda ele alınıyor: https://github.com/sapcc/keppel/blob/7e43d1f6e77ca72f0020645...
      Bildiğim kadarıyla referans uygulama da aynı yaklaşımı kullanıyor. Digest hesaplaması yalnızca sıralı yapılabildiği için upload’ın da sıralı ilerlemesi gerekiyor
      [1] https://github.com/sapcc/keppel
      [2] https://github.com/distribution/distribution
    • Blog yazısı için teşekkürler. “Son 4 ay boyunca Outerbounds ile iş birliği yaparak özel bir konteyner imajı builder’ı geliştirdim” demişsiniz; ayrı bir yazı konusu olduğunu söylemişsiniz ama biraz olsun ayrıntı paylaşabilir misiniz merak ediyorum
      Bir GitHub deposu bağlantısı bile olsa iyi olur. Arka plan şu: $PROGRAMMING_LANGUAGE içinden programatik olarak OCI imajları oluşturmanın bir yolunu arıyorum ya da kendim uygulamayı düşünüyorum. Buildah gibi olsun, ama komut satırı arayüzü değil de gerçek bir programlama dili için API olsun istiyorum. Elbette Buildah’ı alt süreç olarak çağırmak mümkün, ama bu biraz zahmetli; ayrıca Buildah’ın iç durumu ile etkileşmek veya temizlik yapmak gibi konuları da düşünmek gerekiyor ve Buildah şu anda Mac’i de desteklemiyor
    • Aklıma hemen belirgin bir neden gelmiyor; yük kaynaklı olabilir
      Eskiden Docker’a paralel push eklemiş olabilirim, ama pull ile push’u karıştırıyor da olabilirim. Sonradan baktığımda çalışmamın nihai push değil, kontrol paralelleştirme tarafında olduğunu gördüm. Bir katmanın hangi katmanın “üstüne” geldiğini açıkça belirtiyorsa, işaret edilen ID’nin zaten mevcut olması gerektiğinden böyle olabilir
  • Oldukça güzel bir kullanım senaryosu
    Şahsen sadece Nexus kullanıyorum. Yeterince iyi çalışıyor ve OCI imajlarından apt paketlerine, özel Maven, NuGet, npm depolarına kadar pek çok şeyi destekliyor. Ancak yapılandırması ve kaynak kullanımı biraz can sıkıcı; özellikle de temizlik politikaları tarafı: https://www.sonatype.com/products/sonatype-nexus-repository
    Yine de docker pull’un “sadece” bir HEAD ve GET isteği demeti olması gerçekten hoş. Uzun zamandır iyi çalışan şeyi aynen kullanıp gereksiz yere karmaşıklaştırmayan bu tür sağduyulu kararları daha fazla teknolojide görmek isterim. Kimlik doğrulama ve temizlik işlevleri de olan basit konteyner depolarının daha fazla olmaması şaşırtıcı. Nexus ve Harbor, gerçek kullanım için ikisi de epey karmaşık

    • Paketler için yalnızca Gitea kullanıyorum. Docker, npm, Python vb. işliyor
      Bu başlıkta kimsenin bahsetmemiş olmasına şaşırdım
  • CNCF’nin Distribution’ı, eski Docker Registry, registry’yi S3’ten alan CloudFront imzalı URL’lerle destekleme özelliğini içeriyor [1]
    https://distribution.github.io/distribution/storage-drivers/...

  • https://github.com/distribution/distribution ile ilgili sorunun ne olduğunu merak ediyorum

    • Bunu daha önce görmemiştim ve gerçekten S3’ü destekliyor. Ama indirmeleri istemciye doğrudan S3’ten mi sunduğunu, yoksa S3’ü yalnızca kendi depolama backend’i olarak kullanıp pull sırasında fiilen bir proxy gibi mi davrandığını merak ediyorum
  • Bu yöntem oldukça pahalı görünüyor; yazıda maliyet konusunun da ele alınmış olmasını isterdim. Hem S3 hem de R2’yi merak ediyorum

    • S3 Standard katmanı, GB başına depolama maliyeti açısından ECR’nin beşte biri
      Ücretsiz internete giden trafik maliyeti aynı, ancak herkese açık ECR depolarında AWS içi kullanım için dışa giden trafiği ücretsiz yapan bir istisna var
    • Maliyet sonuçta S3 maliyeti. Bölgeye ve depolama katmanına göre değişir, ancak GB başına depolama maliyeti, GET/PUT maliyeti ve bant genişliği maliyeti AWS web sitesinden görülebilir: https://aws.amazon.com/s3/pricing/
  • Geliştirme araçları dışında Docker’ı pek kullanmıyorum, ama neden özel container registry diye bir şeyin var olması gerektiğini hiç anlayamadım
    Bana sadece rant kollama gibi geliyor. Kendi yönettiğiniz bir imaj dosyası benzeri bir şey oluşturup istediğiniz gibi kullanmaya kıyasla pratikte ne gibi bir avantajı var, merak ediyorum

    • Kullanmak zorunda değilsiniz. docker save ve docker import kullanabilirsiniz
      docker save alpine:3.19 > alpine.tar
      docker load < alpine.tar
      Ama bu kez o tar dosyasını yönetmeniz gerekir ve tüm sistemlerin onun nerede olduğunu ve nasıl erişileceğini bilmesi gerekir. Ya da tekerleği yeniden icat etmeden Docker’ın zaten sunduğu yöntemi kullanabilirsiniz
    • Dünyaya açık etmeyeceğiniz imajlarınız olma ihtimali yüksek. Bu tür imajlara genellikle k8s cluster’ları veya CI/CD runner’ları gibi altyapıların erişebilmesi gerekir
      Bu yüzden ya kendi registry’nizi kurmanız ya da birine para ödeyip bunu yaptırmanız gerekir. Elbette imajları yalnızca geliştirme için kullanıyorsanız bunların hiçbiri anlamlı değildir; imajları geliştirme makinesinde saklamanız yeterli
    • Kod deposu yerine dosyaları e-postayla gönderip almamamızın nedeni ile aynı
      Tüm eski sürümlerin bulunduğu ve birden fazla tüketicinin kolayca erişebildiği merkezi bir depoya ihtiyaç vardır. Uygulamayı derledikten sonra çalışabileceği her yere tek tek itmek istemezsiniz. Bir kez derleyip merkezi depoya push edersiniz, her yerin de o depoyu referans almasını sağlarsınız
      Özel depo barındırma için mutlaka para ödemeniz de gerekmez. Kendi kendinize barındırabileceğiniz pek çok araç var
    • Özel cloud registry’ler, Docker imajlarıyla ilgili zorunlu kimlik doğrulama/yetkilendirme gereksinimleri olan projelerde çok kullanışlıdır
      Ortam bazında her şeyi Terraform, Bicep, Pulumi ile yapılandırabilirsiniz
    • Bunu nasıl yöneteceğiniz de ayrı bir mesele. Herkese açık imajlar için kullandığınız araçların aynısını kullanmak istiyorsanız bir container registry işletirsiniz
  • ECR gerçekten de imaj katmanlarını birden çok parça halinde yüklemeye imkân verecek şekilde tasarlanmış gibi görünüyor
    İlgili ECR API’leri arasında her imaj katmanı yüklemesinin başında çağrılan InitiateLayerUpload API, her katman parçası için çağrılan UploadLayerPart API (en fazla 20 MB) ve katman yüklemesinden sonra imaj katmanı referanslarını içeren imaj manifest’ini push eden PutImage API var. Tuhaf olan, katman parçalarını base64 kodlamasıyla yüklemek zorunda olmanız; bu da veriyi yaklaşık %33 artırıyor

    • O API’yi doğrudan denedim; ne yazık ki orada da sıralı yükleme gerektiriyordu
  • Dosya yolu yerleşimini endpoint kontrol mekanizması olarak kullanma fikri ilginç
    Ancak Docker-Content-Digest header’ının nasıl ele alınacağını merak ediyorum. Zorunlu değil ama yanıta eklenmesi öneriliyor; birçok istemci bunu bekler ve header’ı olmayan katmanları reddedebilir. Ayrıca OCI 1.1 spesifikasyonundaki referrers API gibi özellikleri de kaçırabilirsiniz. Uygulaması epey zahmetli olacak gibi