S3 को container registry के रूप में इस्तेमाल करना
(ochagavia.nl)- कंटेनर इमेज आमतौर पर dedicated registry में अपलोड की जाती हैं, लेकिन अगर S3 bucket को HTTP पर एक्सपोज़ किया जाए और फाइलों को तय path पर रखा जाए, तो इसे
docker pulltarget की तरह इस्तेमाल किया जा सकता है - एक 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 तभी जा सकता है जब पिछला पूरा हो जाए
- यह spec वह standard है जो कई registries में
- 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:...
- image manifest मौजूद है या नहीं जाँच:
- अंततः
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 टिप्पणियां
Hacker News की राय
अफसोस है कि OCI Distribution Spec किसी अच्छी तरह डिज़ाइन किए गए specification की तरह नहीं पढ़ता
spec के हिसाब से layer push क्रमवार करनी होती है, इसलिए chunk में upload करने पर भी हर chunk पूरा होने के बाद ही अगले पर जा सकते हैं। DockerHub और GHCR पर किए गए tests के मुताबिक chunk upload खुद भी वैसे ही broken है, और clients हर blob/layer को पूरा का पूरा upload करना पसंद करते हैं। spec
Content-Rangevalue के ऐसे format की भी सिफारिश करता है जो RFC7233 format से मेल नहीं खाताबेशक blob-level parallelism है, लेकिन blob के भीतर parallelism नहीं है। टैग सूची के pagination standardization का मौका चूक जाना भी खलता है। standard से संबंधित wording गलती से हट गई [1], जिसके बाद हर registry ने अपनी-अपनी तरह से implement कर लिया
[1] https://github.com/opencontainers/distribution-spec/issues/4...
ऐसा नहीं कि मैं इसे पूरी तरह कोसना चाहता हूँ। यह सच में revolutionary था, Linux namespaces का इस्तेमाल पहले की तुलना में कहीं आसान बनाया, और दुनिया को बेहतर दिशा में बदला। बस इसने हमेशा technical completeness से ज्यादा user experience को प्राथमिकता दी है, और यह अपने-आप में जरूरी नहीं कि बुरा हो। जैसे कई boring कंपनियाँ Perl या FTP से भेजे-लिए जाने वाले CSV के जरिए महंगी problems solve करती हैं, वैसे ही boring या यहाँ तक कि खराब technology भी अच्छे package में दी जाए तो बड़ी value दे सकती है
फिर भी कभी-कभी यह सोचकर कड़वाहट होती है कि यह अभी से कहीं बेहतर हो सकता था
उदाहरण के लिए
.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
कुछ use cases में यह बड़ी समस्या नहीं हो सकती, लेकिन दूसरे मामलों में यह तुरंत बाहर करने वाली शर्त हो सकती है
मैं ही लेखक हूँ। OCI spec में layer push को sequential होना क्यों ज़रूरी है, अगर किसी को पता हो तो बताएं
जानना चाहता हूँ कि यह बस कोई historical accident है या इसके पीछे कोई छिपी वजह है। साफ़ कर दूँ कि कई layers को parallel में push करना तो बिल्कुल संभव है; यहाँ बात एक single layer के content को sequentially push करने वाली है
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 काफ़ी सीमित लगते हैं
जब 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 को
Locationheader में 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
अगर 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 नहीं करतालगता है कभी 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औरGETrequests का एक set है। जो चीज़ बहुत पहले से अच्छे से काम कर रही है, उसे वैसे ही इस्तेमाल करना और बेवजह complex न बनाना—ऐसे common-sense फैसले और technologies में देखने को मिलें तो अच्छा होगा। authentication और cleanup features वाला simple container storage ज़्यादा न होना surprising है। Nexus और Harbor दोनों practical use में काफ़ी complex हैंहैरानी है कि इस 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 और R2, दोनों को लेकर जिज्ञासा है
मुफ्त इंटरनेट पर बाहर जाने वाले traffic की लागत समान है, लेकिन public ECR repositories में AWS के अंदरूनी उपयोग के लिए बाहर जाने वाले traffic को मुफ्त करने वाला एक exception है
GET/PUTcost, और 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.tardocker load < alpine.tarलेकिन अब उस tar file को manage करना होगा, और सभी systems को उसकी location और access method पता होना चाहिए। नहीं तो पहिया फिर से आविष्कार करने के बजाय Docker द्वारा पहले से दिया गया तरीका इस्तेमाल कर सकते हैं
इसलिए या तो खुद registry बनानी होगी या किसी को पैसे देकर यह काम करवाना होगा। बेशक अगर images सिर्फ development के लिए इस्तेमाल हो रही हैं, तो इन सबका कोई मतलब नहीं है; development machine पर images store कर दें
क्योंकि एक central repository चाहिए जहां सभी previous versions हों और जिसे कई consumers आसानी से access कर सकें। app build करने के बाद उसे हर उस जगह अलग-अलग push नहीं करना चाहते जहां वह run हो सकती है। एक बार build करके central repository में push करें, और सभी जगहों को उसी repository को reference करने दें
private repository hosting के लिए पैसे देना भी जरूरी नहीं है। self-host करने के लिए बहुत सारे tools उपलब्ध हैं
environment के हिसाब से Terraform, Bicep, Pulumi से सब configure किया जा सकता है
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% बढ़ जाता हैfile path layout को endpoint control mechanism के रूप में इस्तेमाल करने का idea दिलचस्प है
हालांकि यह जानना चाहूंगा कि
Docker-Content-Digestheader को कैसे handle किया जाएगा। यह mandatory नहीं है, लेकिन response में शामिल करने की recommendation है, और कई clients इसकी उम्मीद करते हैं तथा header न होने वाली layer को reject कर सकते हैं। साथ ही OCI 1.1 spec की referrers API जैसी functionality छूट सकती है। implementation काफी tricky लगती है