1 पॉइंट द्वारा GN⁺ 2024-07-13 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • कंटेनर इमेज आमतौर पर dedicated registry में अपलोड की जाती हैं, लेकिन अगर S3 bucket को HTTP पर एक्सपोज़ किया जाए और फाइलों को तय path पर रखा जाए, तो इसे docker pull target की तरह इस्तेमाल किया जा सकता है
  • एक custom image builder में pull की जा सकने वाली इमेज को कुछ ही सेकंड में बनाना bottleneck बन गया, और layer push time का बड़ा हिस्सा उसी में जा रहा था
  • 198 MiB layer upload benchmark में ECR की गति 24~28 MiB/s और S3 की 115~190 MiB/s मापी गई, इसलिए S3 अधिकतम 8 गुना तेज था
  • यह अंतर इसलिए आता है क्योंकि S3 एक single layer के chunks को parallel upload कर सकता है, जबकि OCI Distribution Spec आधारित ECR को chunks को क्रमवार प्रोसेस करना पड़ता है
  • यह तरीका docker push, image validity verification, security scanning, private repository access control जैसी registry सुविधाओं का विकल्प नहीं है, इसलिए इसे एक experimental optimization के रूप में देखना चाहिए

S3 bucket से docker pull काम करने की शर्तें

  • S3 को container registry की तरह इस्तेमाल करने के लिए bucket को HTTP पर एक्सपोज़ करना होगा और Docker जिन paths की अपेक्षा करता है, उन पर image files अपलोड करनी होंगी
  • शर्तें पूरी होने पर सामान्य registry की तरह docker pull से इमेज लाई जा सकती है
  • डेमो इमेज cowsay चलाती है, और bucket URL को image name की तरह इस्तेमाल करके docker run --rm .../cowsay के रूप में काम करती है
  • डेमो में Cloudflare R2 का उपयोग किया गया
    • free egress इसका चयन करने का कारण था
    • R2 और S3 API-compatible हैं, इसलिए AWS SDK से R2 पर इमेज अपलोड की गई

dedicated registry की जगह S3 पर विचार क्यों किया गया

  • कंटेनर इमेज सामान्यतः DockerHub, GitHub Container Registry, ECR जैसी dedicated registries पर host की जाती हैं
  • custom image builder का लक्ष्य requirement से pull की जा सकने वाली image तक कुछ ही सेकंड में पहुँचना था
  • AWS environment में ECR सबसे आसान विकल्प है, लेकिन वास्तविक upload speed में S3 और ECR का अंतर काफी बड़ा दिखा
  • performance optimization के लिए code में execution trace जोड़ने पर पता चला कि container registry में layers push करने का समय बड़ा bottleneck है

198 MiB layer upload के नतीजे

  • एक छोटे benchmark में 198 MiB layer को ECR और S3 पर अलग-अलग अपलोड करके समय और throughput की तुलना की गई
  • देखी गई गति इस प्रकार थी
    • ECR: न्यूनतम 24 MiB/s, 8.2 सेकंड
    • ECR: अधिकतम 28 MiB/s, 7.0 सेकंड
    • S3: न्यूनतम 115 MiB/s, 1.7 सेकंड
    • S3: अधिकतम 190 MiB/s, 1.0 सेकंड
  • नतीजों के अनुसार S3, ECR से अधिकतम 8 गुना तेज था
  • प्रयोग AWS में चलाया गया था, और S3 तथा ECR VPC के जरिए internally connected थे, इसलिए public internet से होकर नहीं जाना पड़ा
    • इस स्थिति ने संभवतः बेहतर latency और bandwidth उपलब्ध कराई

parallel chunk upload से बना speed difference

  • S3 upload एक single layer के chunks को parallel में upload कर सकता है
  • पर्याप्त bandwidth होने पर parallel chunk upload से throughput काफी बढ़ जाता है
  • AWS documentation भी bandwidth usage को अधिकतम करने के लिए parallel chunk upload की सिफारिश करता है
  • ECR, OCI Distribution Spec को implement करता है
    • यह spec वह standard है जो कई registries में docker pull और docker push को काम करने देता है
    • layer push को क्रमवार आगे बढ़ना होता है, और chunk upload होने पर भी अगला chunk तभी जा सकता है जब पिछला पूरा हो जाए
  • S3 में भी sequential upload टेस्ट करने पर throughput ECR के समान स्तर तक गिर जाता है

docker pull की वास्तविक request structure

  • docker pull की internal requests कई HEAD और GET requests से मिलकर बनी होती हैं
  • उदाहरण flow इस प्रकार है
    • image manifest मौजूद है या नहीं जाँच: HEAD /v2/my-image/manifests/latest
    • image manifest डाउनलोड: GET /v2/my-image/manifests/latest
    • manifest hash से दोबारा डाउनलोड: GET /v2/my-image/manifests/sha256:...
    • image metadata blob डाउनलोड: GET /v2/my-image/blobs/sha256:...
    • image layer blob डाउनलोड: GET /v2/my-image/blobs/sha256:...
  • अंततः docker pull आवश्यक फाइलों को HTTP से डाउनलोड करने की प्रक्रिया के काफी करीब है
  • अगर static file server आवश्यक फाइलों को अपेक्षित paths पर रखे और हर request के लिए सही Content-Type header सेट करे, तो container image को pull किया जा सकता है
  • S3 bucket इन दोनों शर्तों को पूरा कर सकता है, इसलिए सावधानी से configure करने पर यह container registry की तरह काम कर सकता है

experimental approach की सीमाएँ

  • यह तरीका अभी experimental है, और अधिक जाँच के बिना मजबूत निष्कर्ष निकालना कठिन है
  • S3 सख्त अर्थ में container registry नहीं है
    • docker push नहीं किया जा सकता
    • docker pull का काम करना HTTP request structure और static file serving के मेल का परिणाम है
  • मौजूदा container registries bucket में फाइलें रखने के तरीके से कहीं अधिक सुविधाएँ देती हैं
    • standard push method से अपलोड की गई images के वास्तव में valid होने पर भरोसा किया जा सकता है
    • वे layers के लिए automatic security scanning और warnings दे सकती हैं
    • private repositories के access permissions को native रूप से सेट किया जा सकता है
  • अगर यह अपेक्षा के अनुसार काम करे, तो public container images को Cloudflare R2 पर host करने का तरीका भी संभव हो सकता है

1 टिप्पणियां

 
GN⁺ 2024-07-13
Hacker News की राय
  • अफसोस है कि OCI Distribution Spec किसी अच्छी तरह डिज़ाइन किए गए specification की तरह नहीं पढ़ता
    spec के हिसाब से layer push क्रमवार करनी होती है, इसलिए chunk में upload करने पर भी हर chunk पूरा होने के बाद ही अगले पर जा सकते हैं। DockerHub और GHCR पर किए गए tests के मुताबिक chunk upload खुद भी वैसे ही broken है, और clients हर blob/layer को पूरा का पूरा upload करना पसंद करते हैं। spec Content-Range value के ऐसे format की भी सिफारिश करता है जो RFC7233 format से मेल नहीं खाता
    बेशक blob-level parallelism है, लेकिन blob के भीतर parallelism नहीं है। टैग सूची के pagination standardization का मौका चूक जाना भी खलता है। standard से संबंधित wording गलती से हट गई [1], जिसके बाद हर registry ने अपनी-अपनी तरह से implement कर लिया
    [1] https://github.com/opencontainers/distribution-spec/issues/4...

    • Docker और container के आसपास की technologies आम तौर पर ऐसी ही हैं। user experience के तौर पर Docker शानदार है, लेकिन technology के तौर पर लगभग mess है
      ऐसा नहीं कि मैं इसे पूरी तरह कोसना चाहता हूँ। यह सच में revolutionary था, Linux namespaces का इस्तेमाल पहले की तुलना में कहीं आसान बनाया, और दुनिया को बेहतर दिशा में बदला। बस इसने हमेशा technical completeness से ज्यादा user experience को प्राथमिकता दी है, और यह अपने-आप में जरूरी नहीं कि बुरा हो। जैसे कई boring कंपनियाँ Perl या FTP से भेजे-लिए जाने वाले CSV के जरिए महंगी problems solve करती हैं, वैसे ही boring या यहाँ तक कि खराब technology भी अच्छे package में दी जाए तो बड़ी value दे सकती है
      फिर भी कभी-कभी यह सोचकर कड़वाहट होती है कि यह अभी से कहीं बेहतर हो सकता था
    • इसके अलावा, पता नहीं समस्या OCI spec में है या AWS ही अलग है, लेकिन GitLab या Nexus के उलट AWS ECR folder auto-creation support नहीं करता
      उदाहरण के लिए .dkr.ecr..amazonaws.com/foo/bar/baz:tag जैसा form काम नहीं करता, और सिर्फ flat storage संभव है, जिससे image name या tag जरूरत से ज्यादा लंबे हो जाते हैं। theoretical रूप से Terraform में ECR repository object बनाकर कुछ वैसा imitate किया जा सकता है, लेकिन उन pipelines में यह अच्छा नहीं है जहाँ resulting image path dynamic होता है। CI pipeline के IAM role को असुविधाजनक रूप से बहुत ज्यादा permissions देनी पड़ती हैं, और central Terraform repository के बाहर AWS resources manage होना भी पसंद नहीं आता
      [1] https://stackoverflow.com/questions/64232268/storing-images-...
  • Cloudflare ने कभी R2 इस्तेमाल करने वाला container registry server open source के तौर पर release किया था
    जानना चाहूँगा कि किसी ने इसे इस्तेमाल किया है या नहीं
    [1]: https://github.com/cloudflare/serverless-registry

    • अच्छा दिखता है। हालांकि इसमें प्रति layer 500MB limit बताई गई है
      कुछ use cases में यह बड़ी समस्या नहीं हो सकती, लेकिन दूसरे मामलों में यह तुरंत बाहर करने वाली शर्त हो सकती है
  • मैं ही लेखक हूँ। OCI spec में layer push को sequential होना क्यों ज़रूरी है, अगर किसी को पता हो तो बताएं
    जानना चाहता हूँ कि यह बस कोई historical accident है या इसके पीछे कोई छिपी वजह है। साफ़ कर दूँ कि कई layers को parallel में push करना तो बिल्कुल संभव है; यहाँ बात एक single layer के content को sequentially push करने वाली है

    • शायद इससे cleanup सरल हो जाता है। अगर “आख़िरी” chunk तक नहीं पहुँचे, तो N+Timeout के बाद साफ़ है कि upload पूरा नहीं हुआ, इसलिए उसे हटा सकते हैं
      यानी partial upload को कैसे handle करना है, यह implementation detail सरल हो जाती है। वरना हर chunk के अंत में जांचना पड़ेगा कि बाकी सभी chunks मौजूद हैं या नहीं, और फिर completion process करना होगा। हालांकि यह implementation detail है, और यह meaningful या intentional design था या नहीं, इस पर शक है। S3 वाला approach ठीक काम करेगा लगता है, और पहले एक कंपनी में बड़े images deploy करते समय कुछ ऐसा ही किया था। $0.10 प्रति GB प्रति माह काफ़ी जमा हो जाता था
      ECR की extra features खो देंगे, लेकिन व्यक्तिगत रूप से मुझे वे features काफ़ी सीमित लगते हैं
    • push पर काम नहीं किया है, लेकिन ऐसा approach देखकर अच्छा लगा। Docker के शुरुआती दिनों में usable private registry container नहीं था, इसलिए images को nginx के पीछे रखकर pull करते थे, इसलिए लेख पढ़ना दिलचस्प लगा
    • मैंने OCI-compatible registry [1] implement की है, और spec जटिल होने के कारण ज़्यादातर मामलों में spec के बजाय reference implementation [2] के behavior को follow किया
      जब client blob upload पूरा करता है, तो उसे पूरे blob का digest देना होता है। यह requirement शायद इसलिए है ताकि server मिले हुए bytes की integrity verify कर सके। अगर server digest check अंतिम HTTP request में ही शुरू करे, तो पिछले HTTP requests में storage में लिखे गए blob content को पूरा फिर से पढ़ना पड़ेगा। बड़े layers में यह delay सहना मुश्किल हो सकता है। एक खास client requirement के कारण हमने 150GiB blob तक काम करना verify किया था
      इसके बजाय हमारी implementation में पूरी request sequence के दौरान digest calculation लगातार चलती रहती है। blob data chunks में लेते हुए साथ-साथ digest calculation और blob storage में streaming करते हैं। हर request के बीच digest calculation state को Location header में client को लौटाए जाने वाले upload URL के अंदर serialize कर देते हैं। मोटे तौर पर यह इसी code में handle होता है: https://github.com/sapcc/keppel/blob/7e43d1f6e77ca72f0020645...
      मेरी जानकारी में reference implementation भी यही approach इस्तेमाल करता है। digest calculation केवल sequentially ही संभव है, इसलिए upload भी sequential होना पड़ता है
      [1] https://github.com/sapcc/keppel
      [2] https://github.com/distribution/distribution
    • blog post के लिए धन्यवाद। आपने कहा था, “पिछले 4 महीनों से Outerbounds के साथ collaborate करते हुए custom container image builder develop किया”; आपने इसे अलग post का विषय बताया था, लेकिन क्या कुछ details बता सकते हैं?
      अगर GitHub repository link भी हो तो अच्छा होगा। background यह है कि मैं $PROGRAMMING_LANGUAGE के अंदर programmatically OCI images बनाने का तरीका ढूँढ रहा हूँ, या खुद implement करने की सोच रहा हूँ। Buildah जैसा, लेकिन command-line interface नहीं बल्कि असली programming language के लिए API चाहिए। बेशक Buildah को subprocess के रूप में call किया जा सकता है, लेकिन यह थोड़ा झंझट भरा है, और Buildah की internal state से interact करने या cleanup की समस्या भी देखनी पड़ती है; साथ ही अभी Buildah Mac support नहीं करता
    • कोई स्पष्ट वजह तुरंत याद नहीं आती; यह load-based भी हो सकता है
      लगता है कभी Docker में parallel push जोड़ा था, लेकिन शायद मैं pull और push को मिला रहा हूँ। बाद में देखा तो मेरा काम final push नहीं बल्कि check parallelization की तरफ था। अगर आप specify करते हैं कि कौन-सी layer किस layer “के ऊपर” जाती है, तो जिस ID को reference किया जा रहा है उसका पहले से मौजूद होना ज़रूरी होगा—शायद वजह यह भी हो सकती है
  • काफ़ी शानदार use case है
    व्यक्तिगत रूप से मैं बस Nexus इस्तेमाल करता हूँ। यह पर्याप्त अच्छा चलता है और OCI images से लेकर apt packages, custom Maven, NuGet, npm repositories जैसी चीज़ों तक support करता है। हालांकि setup और resource usage थोड़ा परेशान करते हैं, खासकर cleanup policies: https://www.sonatype.com/products/sonatype-nexus-repository
    फिर भी यह बात सचमुच अच्छी है कि docker pull “बस” HEAD और GET requests का एक set है। जो चीज़ बहुत पहले से अच्छे से काम कर रही है, उसे वैसे ही इस्तेमाल करना और बेवजह complex न बनाना—ऐसे common-sense फैसले और technologies में देखने को मिलें तो अच्छा होगा। authentication और cleanup features वाला simple container storage ज़्यादा न होना surprising है। Nexus और Harbor दोनों practical use में काफ़ी complex हैं

    • मैं packages के लिए सिर्फ़ Gitea इस्तेमाल कर रहा हूँ। Docker, npm, Python वगैरह handle करता है
      हैरानी है कि इस thread में किसी ने इसका ज़िक्र नहीं किया
  • CNCF Distribution, यानी पुराना Docker Registry, में S3 से fetch करने वाले CloudFront signed URLs के ज़रिये registry को back करने की functionality शामिल है [1]
    https://distribution.github.io/distribution/storage-drivers/...

  • जानना चाहता हूँ कि https://github.com/distribution/distribution में क्या समस्या है

    • यह मैंने पहले नहीं देखा था और यह सच में S3 support करता है। लेकिन curious हूँ कि क्या यह client को download सीधे S3 से serve करता है, या S3 को सिर्फ़ अपने storage backend की तरह इस्तेमाल करके pull के समय असल में proxy की तरह काम करता है
  • यह तरीका काफी महंगा लगता है, और अच्छा होता अगर लेख में लागत को भी कवर किया गया होता। S3 और R2, दोनों को लेकर जिज्ञासा है

    • S3 standard tier में storage GB के हिसाब से लागत ECR की एक-पांचवीं है
      मुफ्त इंटरनेट पर बाहर जाने वाले traffic की लागत समान है, लेकिन public ECR repositories में AWS के अंदरूनी उपयोग के लिए बाहर जाने वाले traffic को मुफ्त करने वाला एक exception है
    • लागत आखिरकार S3 की ही लागत है। region और storage tier के हिसाब से अलग होती है, लेकिन GB के हिसाब से storage cost, GET/PUT cost, और bandwidth cost AWS वेबसाइट पर देखी जा सकती है: https://aws.amazon.com/s3/pricing/
  • development tools के बाहर Docker का बहुत इस्तेमाल नहीं करता, लेकिन मुझे कभी समझ नहीं आया कि private container registry क्यों होनी चाहिए
    यह बस rent-seeking जैसा लगता है। अपने हिसाब से इस्तेमाल करने के लिए खुद manage की गई image file जैसी कोई चीज़ बनाने की तुलना में असल में इसका क्या फायदा है, यह जानना चाहूंगा

    • जरूरी नहीं कि इसका इस्तेमाल करें। docker save और docker import इस्तेमाल कर सकते हैं
      docker save alpine:3.19 > alpine.tar
      docker load < alpine.tar
      लेकिन अब उस tar file को manage करना होगा, और सभी systems को उसकी location और access method पता होना चाहिए। नहीं तो पहिया फिर से आविष्कार करने के बजाय Docker द्वारा पहले से दिया गया तरीका इस्तेमाल कर सकते हैं
    • काफी संभावना है कि ऐसी images हों जिन्हें दुनिया के सामने public नहीं करना है। ऐसी images आमतौर पर k8s cluster या CI/CD runner जैसी infrastructure से accessible होनी चाहिए
      इसलिए या तो खुद registry बनानी होगी या किसी को पैसे देकर यह काम करवाना होगा। बेशक अगर images सिर्फ development के लिए इस्तेमाल हो रही हैं, तो इन सबका कोई मतलब नहीं है; development machine पर images store कर दें
    • यह उसी वजह जैसा है कि code repository के बजाय files को email से आदान-प्रदान क्यों नहीं करते
      क्योंकि एक central repository चाहिए जहां सभी previous versions हों और जिसे कई consumers आसानी से access कर सकें। app build करने के बाद उसे हर उस जगह अलग-अलग push नहीं करना चाहते जहां वह run हो सकती है। एक बार build करके central repository में push करें, और सभी जगहों को उसी repository को reference करने दें
      private repository hosting के लिए पैसे देना भी जरूरी नहीं है। self-host करने के लिए बहुत सारे tools उपलब्ध हैं
    • private cloud registry उन projects में Docker images के लिए बहुत उपयोगी है जिनमें जरूरी authentication/authorization requirements होती हैं
      environment के हिसाब से Terraform, Bicep, Pulumi से सब configure किया जा सकता है
    • उसे manage कैसे करना है, यह भी मुद्दा है। अगर public images के लिए इस्तेमाल होने वाले जैसे ही tools इस्तेमाल करने हैं, तो container registry चला सकते हैं
  • ECR वास्तव में ऐसा designed लगता है कि image layers को कई parts में upload किया जा सके
    संबंधित ECR APIs में हर image layer upload की शुरुआत पर call किया जाने वाला InitiateLayerUpload API, हर layer chunk के लिए call किया जाने वाला UploadLayerPart API (maximum 20MB), और layer upload के बाद image layer references वाला image manifest push करने वाला PutImage API शामिल है। अजीब बात यह है कि layer chunks को base64 encoding के साथ upload करना पड़ता है, जिससे data लगभग 33% बढ़ जाता है

    • मैंने उस API को सीधे इस्तेमाल करके देखा है, और दुर्भाग्य से वहां भी ordered upload की requirement थी
  • file path layout को endpoint control mechanism के रूप में इस्तेमाल करने का idea दिलचस्प है
    हालांकि यह जानना चाहूंगा कि Docker-Content-Digest header को कैसे handle किया जाएगा। यह mandatory नहीं है, लेकिन response में शामिल करने की recommendation है, और कई clients इसकी उम्मीद करते हैं तथा header न होने वाली layer को reject कर सकते हैं। साथ ही OCI 1.1 spec की referrers API जैसी functionality छूट सकती है। implementation काफी tricky लगती है