Menggunakan S3 sebagai registry container
(ochagavia.nl)- Image container biasanya diunggah ke registry khusus, tetapi jika bucket S3 diekspos melalui HTTP dan file ditempatkan pada jalur yang telah ditentukan, bucket itu bisa digunakan seperti target
docker pull - Pada custom image builder, proses untuk membuat image yang bisa di-pull dalam hitungan detik menjadi bottleneck, dan waktu push layer mengambil porsi besar
- Pada benchmark upload layer 198 MiB, ECR terukur di 24~28 MiB/s, sedangkan S3 di 115~190 MiB/s, sehingga S3 bisa hingga 8 kali lebih cepat
- Perbedaannya muncul karena S3 dapat melakukan upload paralel untuk chunk dari satu layer, sedangkan ECR berbasis OCI Distribution Spec harus memproses chunk secara berurutan
- Pendekatan ini tidak dapat menggantikan fitur registry seperti
docker push, validasi image, pemindaian keamanan, dan kontrol akses repositori privat, sehingga harus dipandang sebagai optimisasi eksperimental
Kondisi agar docker pull bisa bekerja dari bucket S3
- Untuk menggunakan S3 seperti registry container, bucket harus diekspos melalui HTTP, lalu file image harus diunggah ke jalur yang diharapkan Docker
- Jika syaratnya terpenuhi, image bisa diambil dengan
docker pullseperti pada registry biasa - Image demo menjalankan cowsay, dan bekerja dengan memakai URL bucket sebagai nama image dalam
docker run --rm .../cowsay - Demo menggunakan Cloudflare R2
- Alasan pemilihannya adalah egress gratis
- R2 dan S3 kompatibel pada level API, sehingga image diunggah ke R2 dengan AWS SDK
Mengapa mempertimbangkan S3 alih-alih registry khusus
- Image container biasanya di-host di registry khusus seperti DockerHub, GitHub Container Registry, dan ECR
- Tujuan custom image builder adalah mencapai image yang bisa di-pull dari requirement hanya dalam beberapa detik
- Di lingkungan AWS, ECR adalah pilihan paling mudah, tetapi pada kecepatan upload nyata terlihat perbedaan besar antara S3 dan ECR
- Setelah trace eksekusi ditambahkan untuk optimisasi performa, waktu untuk me-push layer ke registry container teridentifikasi sebagai bottleneck besar
Hasil upload layer 198 MiB
- Benchmark kecil ini membandingkan waktu dan throughput dengan mengunggah layer 198 MiB masing-masing ke ECR dan S3
- Kecepatan yang diamati adalah sebagai berikut
- ECR: minimum 24 MiB/s, 8,2 detik
- ECR: maksimum 28 MiB/s, 7,0 detik
- S3: minimum 115 MiB/s, 1,7 detik
- S3: maksimum 190 MiB/s, 1,0 detik
- Dari hasilnya, S3 dapat hingga 8 kali lebih cepat daripada ECR
- Kode eksperimen dijalankan di AWS, dan S3 serta ECR terhubung secara internal melalui VPC tanpa melewati internet publik
- Kondisi ini memberikan latensi dan bandwidth sebaik mungkin
Perbedaan kecepatan yang dihasilkan upload chunk paralel
- Upload ke S3 dapat mengunggah chunk dari satu layer secara paralel
- Jika bandwidth mencukupi, upload chunk paralel dapat sangat meningkatkan throughput
- Dokumentasi AWS juga merekomendasikan upload chunk paralel untuk memaksimalkan penggunaan bandwidth
- ECR mengimplementasikan OCI Distribution Spec
- Spesifikasi ini adalah standar yang memungkinkan
docker pulldandocker pushbekerja di berbagai registry - Push layer harus berjalan secara berurutan, sehingga walaupun memakai upload chunk, chunk berikutnya baru bisa diproses setelah chunk sebelumnya selesai
- Spesifikasi ini adalah standar yang memungkinkan
- Saat upload berurutan juga diuji pada S3, throughput-nya turun ke level yang mirip dengan ECR
Struktur permintaan aktual dari docker pull
- Permintaan internal
docker pullterdiri dari beberapa request HEAD dan GET - Contoh alurnya adalah sebagai berikut
- Memeriksa keberadaan manifest image:
HEAD /v2/my-image/manifests/latest - Mengunduh manifest image:
GET /v2/my-image/manifests/latest - Mengunduh ulang menggunakan hash manifest:
GET /v2/my-image/manifests/sha256:... - Mengunduh blob metadata image:
GET /v2/my-image/blobs/sha256:... - Mengunduh blob layer image:
GET /v2/my-image/blobs/sha256:...
- Memeriksa keberadaan manifest image:
- Pada akhirnya,
docker pullpada dasarnya mirip proses mengunduh file yang diperlukan lewat HTTP - Jika static file server menempatkan file yang diperlukan di jalur yang diharapkan dan menetapkan header Content-Type yang sesuai untuk tiap request, image container bisa di-pull
- Bucket S3 dapat memenuhi dua syarat ini, sehingga dengan konfigurasi yang hati-hati dapat berfungsi seperti registry container
Batasan pendekatan eksperimental ini
- Pendekatan ini masih eksperimental, dan sulit menarik kesimpulan kuat sebelum ada penyelidikan lebih lanjut
- S3 bukan registry container dalam arti yang ketat
- Tidak bisa melakukan
docker push docker pullbisa bekerja karena struktur request HTTP dan cara penyajian static file kebetulan cocok
- Tidak bisa melakukan
- Registry container yang ada menyediakan jauh lebih banyak fitur dibanding sekadar mengunggah file ke bucket
- Image yang diunggah lewat metode push standar dapat dipercaya benar-benar valid
- Registry dapat menyediakan pemindaian keamanan otomatis dan peringatan untuk layer
- Hak akses ke repositori privat dapat ditetapkan secara native
- Jika benar bekerja seperti yang diharapkan, pendekatan ini juga bisa memungkinkan hosting image container publik di Cloudflare R2
1 komentar
Pendapat di Hacker News
OCI Distribution Spec terasa disayangkan karena tidak terbaca seperti spesifikasi yang dirancang dengan baik
Menurut spesifikasi, push layer harus dilakukan secara berurutan, jadi meskipun diunggah dalam chunk, setiap chunk harus selesai sebelum bisa lanjut ke chunk berikutnya. Berdasarkan pengujian di DockerHub dan GHCR, upload chunk itu sendiri pada dasarnya memang rusak, dan klien cenderung mengunggah setiap blob/layer secara utuh. Spesifikasi juga merekomendasikan format nilai
Content-Rangeyang tidak sesuai dengan format RFC7233Memang ada paralelisme pada tingkat blob, tetapi tidak ada paralelisme di dalam blob. Selain itu, saya juga kecewa karena kesempatan untuk menstandarkan pagination pada daftar tag terlewatkan. Karena teks terkait secara tidak sengaja dihapus dari standar [1], tiap registry akhirnya mengimplementasikannya dengan cara masing-masing
[1] https://github.com/opencontainers/distribution-spec/issues/4...
Meski begitu, saya tidak bermaksud sepenuhnya mengecamnya. Docker memang revolusioner, membuat penggunaan namespace Linux jauh lebih mudah daripada sebelumnya, dan mengubah dunia ke arah yang lebih baik. Hanya saja, mereka selalu memprioritaskan pengalaman pengguna dibanding kesempurnaan teknis, dan itu sendiri belum tentu buruk. Seperti banyak perusahaan membosankan yang memecahkan masalah mahal dengan Perl atau CSV yang dikirim lewat FTP, teknologi yang membosankan atau bahkan buruk pun bisa bernilai besar jika dikemas dengan baik
Namun sesekali tetap terasa pahit, karena seharusnya ini bisa jauh lebih baik daripada sekarang
Misalnya, bentuk seperti
.dkr.ecr..amazonaws.com/foo/bar/baz:tagtidak bisa digunakan; hanya penyimpanan datar yang memungkinkan, sehingga nama image atau tag menjadi terlalu panjang. Secara teori, kita bisa membuat objek repositori ECR di Terraform untuk menirunya, tetapi ini kurang cocok untuk pipeline yang path image hasilnya dinamis. Peran IAM untuk pipeline CI harus diberi izin yang sangat banyak sampai terasa merepotkan, dan saya juga tidak suka resource AWS dikelola di luar repositori Terraform pusat[1] https://stackoverflow.com/questions/64232268/storing-images-...
Cloudflare pernah merilis secara open source server registry container yang menggunakan R2
Saya penasaran apakah ada yang pernah mencobanya
[1]: https://github.com/cloudflare/serverless-registry
Untuk sebagian penggunaan, ini mungkin bukan masalah besar, tetapi untuk kasus lain bisa langsung menjadi alasan untuk tidak menggunakannya
Saya penulisnya. Kalau ada yang tahu kenapa dalam spesifikasi OCI push layer harus bersifat berurutan, saya ingin diberi tahu
Saya penasaran apakah ini sekadar kebetulan historis, atau ada alasan tersembunyi. Untuk memperjelas, beberapa layer tentu saja bisa di-push secara paralel; yang saya maksud di sini adalah bagian yang mengharuskan isi satu layer tunggal di-push secara berurutan
N+Timeout, jadi tinggal dihapusDengan kata lain, ini menyederhanakan detail implementasi soal bagaimana menangani upload parsial. Kalau tidak, di akhir setiap chunk harus dicek apakah semua chunk lain sudah ada lalu menandainya selesai. Namun ini hanyalah detail implementasi, dan saya ragu apakah itu desain yang bermakna atau disengaja. Pendekatan S3 tampaknya akan bekerja dengan baik, dan dulu saya pernah melakukan sesuatu yang mirip di sebuah perusahaan yang men-deploy image besar. 0,10 dolar per GB per bulan menumpuk cukup besar
Fitur tambahan ECR memang hilang, tetapi secara pribadi saya menganggap fitur-fitur itu cukup terbatas
Saat klien menyelesaikan upload blob, klien harus menyediakan digest dari seluruh blob. Persyaratan ini tampaknya dimaksudkan agar server bisa memverifikasi integritas byte yang diterimanya. Jika server baru mulai memeriksa digest pada request HTTP terakhir, server harus membaca ulang seluruh isi blob yang sudah ditulis ke storage pada request-request HTTP sebelumnya. Untuk layer besar, latensi ini bisa sulit ditoleransi. Karena kebutuhan klien tertentu, kami memverifikasi agar bisa bekerja hingga blob 150GiB
Sebagai gantinya, dalam implementasi kami, perhitungan digest terus berjalan di sepanjang seluruh rangkaian request. Sambil menerima data blob per chunk, kami sekaligus menghitung digest dan melakukan streaming ke storage blob. Di antara setiap request, status perhitungan digest diserialisasi ke dalam URL upload yang dikembalikan ke klien lewat header
Location. Kira-kira ditangani di kode ini: https://github.com/sapcc/keppel/blob/7e43d1f6e77ca72f0020645...Sejauh yang saya tahu, implementasi referensi juga memakai pendekatan yang sama. Karena perhitungan digest hanya bisa dilakukan secara berurutan, upload juga harus berjalan berurutan
[1] https://github.com/sapcc/keppel
[2] https://github.com/distribution/distribution
Tautan repo GitHub saja juga bagus. Latar belakangnya, saya sedang mencari cara untuk membuat image OCI secara terprogram dari dalam
$PROGRAMMING_LANGUAGE, atau mempertimbangkan untuk mengimplementasikannya sendiri. Saya ingin sesuatu seperti Buildah, tetapi berupa API untuk bahasa pemrograman sungguhan, bukan antarmuka baris perintah. Tentu saja Buildah bisa dipanggil sebagai subprocess, tetapi itu agak merepotkan, dan juga perlu memikirkan interaksi dengan state internal Buildah serta pembersihannya; saat ini Buildah juga tidak mendukung MacDulu saya rasa pernah menambahkan push paralel ke Docker, tetapi mungkin saya tertukar dengan pull dan push. Setelah saya lihat lagi, pekerjaan saya ternyata bukan push akhir, melainkan paralelisasi pemeriksaan. Kalau harus menyatakan layer mana berada “di atas” layer mana, mungkin karena ID yang dirujuk harus sudah ada terlebih dahulu
Ini use case yang cukup keren
Secara pribadi saya hanya memakai Nexus. Cukup berjalan dengan baik dan mendukung berbagai hal mulai dari image OCI hingga paket apt, repositori Maven kustom, NuGet, dan npm. Namun konfigurasi dan penggunaan resource-nya agak menyebalkan, terutama di bagian kebijakan pembersihan: https://www.sonatype.com/products/sonatype-nexus-repository
Meski begitu, fakta bahwa
docker pull“hanya” merupakan sekumpulan requestHEADdanGETbenar-benar bagus. Saya ingin melihat lebih banyak teknologi mengambil keputusan masuk akal seperti ini: memakai apa yang sudah bekerja dengan baik sejak lama, dan tidak membuatnya rumit tanpa alasan. Agak mengejutkan bahwa tidak ada lebih banyak storage container sederhana yang juga punya autentikasi dan fitur pembersihan. Nexus dan Harbor sama-sama cukup rumit untuk dipakai dalam praktikSaya terkejut tidak ada yang menyebutnya di thread ini
Distribution dari CNCF, dulu Docker Registry, menyertakan fitur untuk mendukung registry dengan URL bertanda tangan CloudFront yang mengambil dari S3 [1]
https://distribution.github.io/distribution/storage-drivers/...
Saya penasaran apa masalahnya dengan https://github.com/distribution/distribution
Metode ini terlihat sangat mahal, dan rasanya akan bagus kalau tulisan tersebut juga membahas biaya. Saya penasaran dengan S3 maupun R2
Biaya traffic keluar ke internet gratis sama, tetapi repositori ECR publik punya pengecualian: traffic keluar untuk penggunaan internal AWS digratiskan
GET/PUT, dan biaya bandwidth bisa dilihat di situs web AWS: https://aws.amazon.com/s3/pricing/Di luar alat pengembangan, saya tidak banyak memakai Docker, tetapi saya tidak pernah benar-benar memahami mengapa private container registry harus ada
Bagi saya itu terlihat seperti sekadar rent-seeking. Saya penasaran apa keuntungan nyatanya dibanding membuat semacam file image yang dikelola sendiri lalu dipakai sesuka hati
docker savedandocker importdocker save alpine:3.19 > alpine.tardocker load < alpine.tarNamun sekarang Anda harus mengelola file tar itu, dan semua sistem harus tahu lokasinya serta cara mengaksesnya. Atau, Anda bisa memakai cara yang sudah disediakan Docker tanpa menemukan ulang roda
Karena itu, Anda perlu membuat registry sendiri atau membayar pihak lain untuk mengelolanya. Tentu saja, jika image hanya dipakai untuk pengembangan, semua ini tidak relevan; cukup simpan image di mesin pengembangan
Karena kita membutuhkan repositori pusat yang menyimpan semua versi sebelumnya dan mudah diakses oleh banyak konsumen. Kita tidak ingin, setelah membangun aplikasi, mendorongnya satu per satu ke semua tempat tempat aplikasi itu bisa dijalankan. Cukup build sekali, push ke repositori pusat, lalu biarkan semua tempat merujuk ke repositori itu
Anda juga tidak harus membayar untuk hosting repositori privat. Ada banyak alat yang bisa di-host sendiri
Semuanya bisa dikonfigurasi per environment dengan Terraform, Bicep, atau Pulumi
ECR sebenarnya terlihat dirancang agar layer image bisa diunggah dalam beberapa bagian
API ECR terkait mencakup
InitiateLayerUpload API, yang dipanggil saat memulai upload tiap layer image;UploadLayerPart API, yang dipanggil untuk tiap chunk layer (maksimum 20 MB); danPutImage API, yang mendorong manifest image berisi referensi layer image setelah upload layer selesai. Yang aneh adalah chunk layer harus diunggah dengan encoding base64, sehingga data bertambah sekitar 33%Gagasan memakai tata letak path file sebagai sarana kontrol endpoint menarik
Namun saya penasaran bagaimana header
Docker-Content-Digestakan ditangani. Memang tidak wajib, tetapi disarankan untuk disertakan dalam respons, dan banyak klien mengharapkannya serta bisa menolak layer yang tidak memiliki header tersebut. Selain itu, fitur seperti referrers API dalam spesifikasi OCI 1.1 bisa terlewat. Implementasinya tampaknya cukup rumit