1 poin oleh GN⁺ 2024-07-13 | 1 komentar | Bagikan ke WhatsApp
  • 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 pull seperti 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 pull dan docker push bekerja di berbagai registry
    • Push layer harus berjalan secara berurutan, sehingga walaupun memakai upload chunk, chunk berikutnya baru bisa diproses setelah chunk sebelumnya selesai
  • 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 pull terdiri 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:...
  • Pada akhirnya, docker pull pada 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 pull bisa bekerja karena struktur request HTTP dan cara penyajian static file kebetulan cocok
  • 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

 
GN⁺ 2024-07-13
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-Range yang tidak sesuai dengan format RFC7233
    Memang 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...

    • Docker dan teknologi di sekitar container umumnya memang seperti ini. Docker sebagai pengalaman pengguna itu hebat, tetapi sebagai teknologi hampir bisa dibilang berantakan
      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
    • Selain itu, entah ini masalah spesifikasi OCI atau AWS yang memang unik, tetapi berbeda dengan GitLab atau Nexus, AWS ECR tidak mendukung pembuatan folder otomatis
      Misalnya, bentuk seperti .dkr.ecr..amazonaws.com/foo/bar/baz:tag tidak 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

    • Kelihatannya bagus. Namun tertulis ada batas 500MB per layer
      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

    • Mungkin karena membuat pembersihan jadi sederhana. Kalau belum mencapai chunk “terakhir”, jelas itu adalah upload yang belum selesai setelah N+Timeout, jadi tinggal dihapus
      Dengan 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
    • Saya belum pernah menangani push, tetapi pendekatan seperti ini menyenangkan untuk dilihat. Pada masa awal Docker, belum ada kontainer private registry yang layak dipakai, jadi kami meletakkan image di belakang nginx dan melakukan pull, sehingga saya membaca tulisannya dengan tertarik
    • Saya pernah mengimplementasikan registry yang kompatibel dengan OCI [1], dan karena spesifikasinya rumit, sebagian besar perilaku mengikuti implementasi referensi [2] ketimbang spesifikasinya
      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
    • Terima kasih untuk posting blognya. Anda mengatakan “selama 4 bulan terakhir saya bekerja sama dengan Outerbounds untuk mengembangkan builder image container kustom”; meski disebut sebagai bahan tulisan terpisah, saya penasaran apakah Anda bisa membagikan sedikit detail
      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 Mac
    • Tidak ada alasan jelas yang langsung terpikir, mungkin juga berbasis beban
      Dulu 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 request HEAD dan GET benar-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 praktik

    • Saya memakai Gitea hanya untuk paket. Ia menangani Docker, npm, Python, dan lain-lain
      Saya 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

    • Saya belum pernah melihat ini sebelumnya, dan memang mendukung S3. Namun saya penasaran apakah ia menyajikan download langsung dari S3 kepada klien, atau hanya memakai S3 sebagai backend storage internal sehingga saat pull pada dasarnya bertindak seperti proxy
  • Metode ini terlihat sangat mahal, dan rasanya akan bagus kalau tulisan tersebut juga membahas biaya. Saya penasaran dengan S3 maupun R2

    • Tier standar S3, berdasarkan biaya per GB penyimpanan, adalah seperlima dari ECR
      Biaya traffic keluar ke internet gratis sama, tetapi repositori ECR publik punya pengecualian: traffic keluar untuk penggunaan internal AWS digratiskan
    • Pada akhirnya biayanya adalah biaya S3. Tergantung region dan tier penyimpanan, tetapi biaya penyimpanan per GB, biaya 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

    • Tidak wajib memakainya. Anda bisa memakai docker save dan docker import
      docker save alpine:3.19 > alpine.tar
      docker load < alpine.tar
      Namun 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
    • Besar kemungkinan ada image yang tidak ingin Anda publikasikan ke dunia. Image seperti itu biasanya harus bisa diakses dari infrastruktur seperti klaster k8s atau runner CI/CD
      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
    • Alasannya sama seperti mengapa kita tidak saling mengirim file lewat email sebagai pengganti repositori kode
      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
    • Private cloud registry sangat berguna pada proyek yang memiliki kebutuhan autentikasi/otorisasi wajib terkait image Docker
      Semuanya bisa dikonfigurasi per environment dengan Terraform, Bicep, atau Pulumi
    • Cara mengelolanya juga menjadi persoalan. Jika ingin memakai alat yang sama seperti untuk image publik, jalankan saja container registry
  • 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); dan PutImage 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%

    • Saya pernah memakai API itu secara langsung, dan sayangnya di sana pun tetap mensyaratkan upload berurutan
  • Gagasan memakai tata letak path file sebagai sarana kontrol endpoint menarik
    Namun saya penasaran bagaimana header Docker-Content-Digest akan 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