Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Kami memiliki 2 kantong rumput, 75 tablet mescaline lingkungan unix, repositori buruh pelabuhan dan tugas mengimplementasikan perintah docker pull dan docker Push tanpa klien buruh pelabuhan.

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

UPD:
Pertanyaan: Untuk apa semua ini?
Jawaban: Memuat pengujian produk (TIDAK menggunakan bash, skrip disediakan untuk tujuan pendidikan). Diputuskan untuk tidak menggunakan klien buruh pelabuhan untuk mengurangi lapisan tambahan (dalam batas wajar) dan, karenanya, meniru beban yang lebih tinggi. Hasilnya, semua penundaan sistem klien Docker telah dihapus. Kami menerima muatan yang relatif bersih langsung pada produk.
Artikel ini menggunakan alat versi GNU.

Pertama, mari kita cari tahu apa yang dilakukan perintah-perintah ini.

Jadi untuk apa docker pull digunakan? Berdasarkan dokumentasi:

"Tarik gambar atau repositori dari registri".

Di sana kami juga menemukan tautan ke memahami gambar, kontainer, dan driver penyimpanan.

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Dari sini kita dapat memahami bahwa image docker adalah sekumpulan layer tertentu yang berisi informasi tentang perubahan terbaru pada image, yang tentunya kita butuhkan. Selanjutnya kita lihat API registri.

Dikatakan sebagai berikut:

"Sebuah" gambar "adalah kombinasi manifes JSON dan file lapisan individual. Proses menarik gambar > berpusat pada pengambilan kedua komponen ini."

Jadi langkah pertama menurut dokumentasi adalah β€œMenarik Manifes Gambar".

Tentu saja kami tidak akan memotretnya, tetapi kami memerlukan data darinya. Berikut contoh permintaannya: GET /v2/{name}/manifests/{reference}

"Nama dan parameter referensi mengidentifikasi gambar dan diperlukan. Referensi dapat menyertakan tag atau intisari."

Repositori buruh pelabuhan kami diterapkan secara lokal, mari coba jalankan permintaan:

curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/manifests/1.1.1" -H "header_if_needed"

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Sebagai tanggapan, kami menerima json yang saat ini kami hanya tertarik pada jalur hidup, atau lebih tepatnya hashnya. Setelah menerimanya, kita dapat memeriksa masing-masingnya dan menjalankan permintaan berikut: "GET /v2/{name}/blobs/{digest}"

β€œAkses ke suatu lapisan akan dibatasi berdasarkan nama repositori tetapi diidentifikasi secara unik di registri berdasarkan intisari.”

intisari dalam hal ini adalah hash yang kami terima.

Mencoba

curl -s -X GET "http://localhost:8081/link/to/docker/registry/v2/centos-11-10/blobs/sha256:f972d139738dfcd1519fd2461815651336ee25a8b54c358834c50af094bb262f" -H "header_if_needed" --output firstLayer

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Mari kita lihat file seperti apa yang akhirnya kita terima sebagai penyelamat pertama.

file firstLayer

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

itu. rails adalah arsip tar, membongkarnya dalam urutan yang sesuai kita akan mendapatkan konten gambar.

Mari kita tulis skrip bash kecil agar semua ini bisa diotomatisasi

#!/bin/bash -eu

downloadDir=$1
# url as http://localhost:8081/link/to/docker/registry
url=$2
imageName=$3
tag=$4

# array of layers
layers=($(curl -s -X GET "$url/v2/$imageName/manifests/$tag" | grep -oP '(?<=blobSum" : ").+(?=")'))

# download each layer from array
for layer in "${layers[@]}"; do
    echo "Downloading ${layer}"
    curl -v -X GET "$url/v2/$imageName/blobs/$layer" --output "$downloadDir/$layer.tar"
done

# find all layers, untar them and remove source .tar files
cd "$downloadDir" && find . -name "sha256:*" -exec tar xvf {} ;
rm sha256:*.tar
exit 0

Sekarang kita dapat menjalankannya dengan parameter yang diinginkan dan mendapatkan konten gambar yang diperlukan

./script.sh dirName β€œhttp://localhost:8081/link/to/docker/registry” myAwesomeImage 1.0

Bagian 2 - dorongan buruh pelabuhan

Ini akan menjadi sedikit lebih rumit.

Mari kita mulai lagi dengan dokumentasi. Jadi kita perlu mendownload setiap pemimpin, mengumpulkan manifes yang sesuai dan mendownloadnya juga. Tampaknya sederhana.

Setelah mempelajari dokumentasinya, kita dapat membagi proses pengunduhan menjadi beberapa langkah:

  • Inisialisasi proses - "POST /v2/{repoName}/blobs/uploads/"
  • Mengunggah lifeline (kami akan menggunakan unggahan monolitik, yaitu kami mengirimkan setiap lifeline secara keseluruhan) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Panjang Konten: {ukuran lapisan}
    Tipe-Konten: aplikasi/aliran oktet
    Lapisan Data Biner".
  • Memuat manifes - "PUT /v2/{repoName}/manifests/{reference}".

Namun dokumentasinya melewatkan satu langkah, yang tanpanya tidak ada yang akan berhasil. Untuk pemuatan monolitik, serta untuk pemuatan parsial (terpotong), sebelum memuat rel, Anda harus melakukan permintaan PATCH:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Panjang Konten: {ukuran potongan}
Tipe-Konten: aplikasi/aliran oktet
{Data Biner Potongan Lapisan}".

Jika tidak, Anda tidak akan bisa melampaui poin pertama, karena... Alih-alih kode respons yang diharapkan 202, Anda akan menerima 4xx.

Sekarang algoritmanya terlihat seperti:

  • Inisialisasi
  • Rel tambalan
  • Memuat pegangan
  • Memuat manifes
    Poin 2 dan 3, masing-masing, akan diulangi sebanyak jumlah baris yang perlu dimuat.

Pertama, kita membutuhkan gambar apa pun. Saya akan menggunakan archlinux: terbaru

docker pull archlinux

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Sekarang mari simpan secara lokal untuk analisis lebih lanjut

docker save c24fe13d37b9 -o savedArch

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Buka paket arsip yang dihasilkan ke direktori saat ini

tar xvf savedArch

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Seperti yang Anda lihat, setiap jalur hidup berada dalam folder terpisah. Sekarang mari kita lihat struktur manifes yang kami terima

cat manifest.json | json_pp

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Tidak banyak. Mari kita lihat manifes apa yang diperlukan untuk memuat, menurut dokumentasi.

Menerapkan perintah docker pull dan docker push tanpa klien buruh pelabuhan menggunakan permintaan HTTP

Jelas sekali, manifesto yang ada tidak cocok untuk kami, jadi kami akan membuatnya sendiri dengan blackjack dan pelacur, garis hidup, dan konfigurasi.

Kami akan selalu memiliki setidaknya satu file konfigurasi dan serangkaian jalur hidup. Skema versi 2 (saat ini pada saat penulisan), mediaType tidak akan berubah:

echo β€˜{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   "config": {
      "mediaType": "application/vnd.docker.container.image.v1+json",
      "size": config_size,
      "digest": "config_hash"
   },
   "layers": [
      ’ > manifest.json

Setelah membuat manifes dasar, Anda perlu mengisinya dengan data yang valid. Untuk melakukan ini, kita menggunakan template json dari objek rail:

{
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": ${layersSizes[$i]},
         "digest": "sha256:${layersNames[$i]}"
      },

Kami akan menambahkannya ke manifes untuk setiap rel.

Selanjutnya, kita perlu mengetahui ukuran file konfigurasi dan mengganti stub di manifes dengan data sebenarnya

sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile

Sekarang Anda dapat memulai proses pengunduhan dan menyimpan uuid, yang akan menyertai semua permintaan berikutnya.

Script lengkapnya terlihat seperti ini:

#!/bin/bash -eux

imageDir=$1
# url as http://localhost:8081/link/to/docker/registry
url=$2
repoName=$3
tag=$4
manifestFile=$(readlink -f ${imageDir}/manifestCopy)
configFile=$(readlink -f $(find $imageDir -name "*.json" ! -name "manifest.json"))

# calc layers sha 256 sum, rename them accordingly, and add info about each to manifest file
function prepareLayersForUpload() {
  info_file=$imageDir/info
  # lets calculate layers sha256 and use it as layers names further
  layersNames=($(find $imageDir -name "layer.tar" -exec shasum -a 256 {} ; | cut -d" " -f1))

  # rename layers according to shasums. !!!Set required amount of fields for cut command!!!
  # this part definitely can be done easier but i didn't found another way, sry
  find $imageDir -name "layer.tar" -exec bash -c 'mv {} "$(echo {} | cut -d"/" -f1,2)/$(shasum -a 256 {} | cut -d" " -f1)"' ;

  layersSizes=($(find $imageDir -name "*.tar" -exec ls -l {} ; | awk '{print $5}'))

  for i in "${!layersNames[@]}"; do
    echo "{
         "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
         "size": ${layersSizes[$i]},
         "digest": "sha256:${layersNames[$i]}"
      }," >> $manifestFile
  done
  # remove last ','
  truncate -s-2 $manifestFile
  # add closing brakets to keep json consistent
  printf "nt]n}" >> $manifestFile
}

# calc config sha 256 sum and add info about it to manifest
function setConfigProps() {
  configSize=$(ls -l $configFile | awk '{print $5}')
  configName=$(basename $configFile | cut -d"." -f1)

  sed -i "s/config_size/$configSize/g; s/config_hash/$configName/g" $manifestFile
}

#prepare manifest file
prepareLayersForUpload
setConfigProps
cat $manifestFile

# initiate upload and get uuid
uuid=$(curl -s -X POST -I "$url/v2/$repoName/blobs/uploads/" | grep -oP "(?<=Docker-Upload-Uuid: ).+")

# patch layers
# in data-binary we're getting absolute path to layer file
for l in "${!layersNames[@]}"; do
  pathToLayer=$(find $imageDir -name ${layersNames[$l]} -exec readlink -f {} ;)
    curl -v -X PATCH "$url/v2/$repoName/blobs/uploads/$uuid" 
  -H "Content-Length: ${layersSizes[$i]}" 
  -H "Content-Type: application/octet-stream" 
  --data-binary "@$pathToLayer"

# put layer
  curl -v -X PUT "$url/v2/$repoName/blobs/uploads/$uuid?digest=sha256:${layersNames[$i]}" 
  -H 'Content-Type: application/octet-stream' 
  -H "Content-Length: ${layersSizes[$i]}" 
  --data-binary "@$pathToLayer"
done

# patch and put config after all layers
curl -v -X PATCH "$url/v2/$repoName/blobs/uploads/$uuid" 
  -H "Content-Length: $configSize" 
  -H "Content-Type: application/octet-stream" 
  --data-binary "@$configFile"

  curl -v -X PUT "$url/v2/$repoName/blobs/uploads/$uuid?digest=sha256:$configName" 
  -H 'Content-Type: application/octet-stream' 
  -H "Content-Length: $configSize" 
  --data-binary "@$configFile"

# put manifest
curl -v -X PUT "$url/v2/$repoName/manifests/$tag" 
  -H 'Content-Type: application/vnd.docker.distribution.manifest.v2+json' 
  --data-binary "@$manifestFile"

exit 0

kita dapat menggunakan skrip yang sudah jadi:

./uploadImage.sh "~/path/to/saved/image" "http://localhost:8081/link/to/docker/registry" myRepoName 1.0

UPD:
Apa yang kami dapatkan sebagai hasilnya?
Pertama, data nyata untuk analisis, karena pengujian dijalankan di blazemeter dan data permintaan klien buruh pelabuhan tidak terlalu informatif, tidak seperti permintaan HTTP murni.

Kedua, transisi ini memungkinkan kami meningkatkan jumlah pengguna virtual untuk unggahan buruh pelabuhan sekitar 150% dan mendapatkan waktu respons rata-rata 20-25% lebih cepat. Untuk pengunduhan buruh pelabuhan, kami berhasil meningkatkan jumlah pengguna sebesar 500%, sementara waktu respons rata-rata menurun sekitar 60%.

Terima kasih atas perhatian Anda.

Sumber: www.habr.com

Tambah komentar