Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Kami mempunyai 2 beg rumput, 75 tablet mescaline persekitaran unix, repositori docker dan tugas melaksanakan perintah tarik docker dan push docker tanpa klien docker.

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

UPD:
Soalan: Untuk apa semua ini?
Jawapan: Muatkan ujian produk (TIDAK menggunakan bash, skrip disediakan untuk tujuan pendidikan). Diputuskan untuk tidak menggunakan klien docker untuk mengurangkan lapisan tambahan (dalam had yang munasabah) dan, dengan itu, meniru beban yang lebih tinggi. Akibatnya, semua kelewatan sistem klien Docker telah dialih keluar. Kami menerima beban yang agak bersih secara langsung pada produk.
Artikel itu menggunakan alat versi GNU.

Mula-mula, mari kita fikirkan apa yang dilakukan oleh arahan ini.

Jadi untuk apa tarikan buruh pelabuhan digunakan? mengikut dokumentasi:

"Tarik imej atau repositori daripada pendaftaran".

Di sana kami juga mencari pautan ke memahami imej, bekas dan pemacu storan.

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Dari sini kita dapat memahami bahawa imej docker ialah satu set lapisan tertentu yang mengandungi maklumat tentang perubahan terkini dalam imej, yang jelas adalah apa yang kita perlukan. Seterusnya kita lihat API pendaftaran.

Ia mengatakan perkara berikut:

"Imej" ialah gabungan fail manifes JSON dan lapisan individu. Proses menarik > imej tertumpu pada mendapatkan semula dua komponen ini."

Jadi langkah pertama mengikut dokumentasi ialah "Menarik Manifes Imej".

Sudah tentu, kami tidak akan menembaknya, tetapi kami memerlukan data daripadanya. Berikut adalah contoh permintaan: GET /v2/{name}/manifests/{reference}

"Nama dan parameter rujukan mengenal pasti imej dan diperlukan. Rujukan mungkin termasuk teg atau ringkasan."

Repositori docker kami digunakan secara tempatan, mari cuba laksanakan permintaan:

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

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Sebagai tindak balas, kami menerima json yang pada masa ini kami hanya berminat dengan talian hayat, atau lebih tepatnya cincang mereka. Setelah menerimanya, kami boleh meneruskan setiap satu dan melaksanakan permintaan berikut: "GET /v2/{name}/blobs/{digest}"

"Akses kepada lapisan akan berpagar dengan nama repositori tetapi dikenal pasti secara unik dalam pendaftaran melalui ringkasan."

digest dalam kes ini ialah cincangan yang kami terima.

Mencuba

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

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Mari lihat jenis fail yang akhirnya kami terima sebagai talian hayat pertama.

file firstLayer

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

mereka. rel adalah arkib tar, membongkarnya dalam susunan yang sesuai kita akan mendapat kandungan imej.

Mari kita tulis skrip bash kecil supaya semua ini boleh diautomasikan

#!/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 boleh menjalankannya dengan parameter yang dikehendaki dan mendapatkan kandungan imej yang diperlukan

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

Bahagian 2 - docker push

Ini akan menjadi sedikit lebih rumit.

Mari kita mulakan semula dengan dokumentasi. Jadi kita perlu memuat turun setiap pemimpin, mengumpul manifes yang sepadan dan memuat turunnya juga. Nampak mudah.

Selepas mengkaji dokumentasi, kami boleh membahagikan proses muat turun kepada beberapa langkah:

  • Permulaan proses - "POST /v2/{repoName}/blobs/uploads/"
  • Memuat naik talian hayat (kami akan menggunakan muat naik monolitik, iaitu kami menghantar setiap talian hayat secara keseluruhan) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Panjang Kandungan: {saiz lapisan}
    Jenis Kandungan: aplikasi/strim oktet
    Data Perduaan Lapisan".
  • Memuatkan manifes - "PUT /v2/{repoName}/manifests/{reference}".

Tetapi dokumentasi terlepas satu langkah, tanpanya tiada apa yang akan berfungsi. Untuk pemuatan monolitik, serta untuk separa (potongan), sebelum memuatkan rel, anda mesti melakukan permintaan PATCH:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Panjang Kandungan: {size of chunk}
Jenis Kandungan: aplikasi/strim oktet
{Layer Chunk Binary Data}".

Jika tidak, anda tidak akan dapat bergerak melepasi titik pertama, kerana... Daripada kod respons yang dijangkakan 202, anda akan menerima 4xx.

Sekarang algoritma kelihatan seperti:

  • Permulaan
  • Rel tampalan
  • Memuatkan susur tangan
  • Memuatkan manifes
    Mata 2 dan 3, masing-masing, akan diulang seberapa banyak bilangan baris yang perlu dimuatkan.

Pertama, kita memerlukan sebarang imej. Saya akan menggunakan archlinux:latest

docker pull archlinux

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Sekarang mari simpannya secara tempatan untuk analisis lanjut

docker save c24fe13d37b9 -o savedArch

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Buka pek arkib yang terhasil ke dalam direktori semasa

tar xvf savedArch

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Seperti yang anda lihat, setiap talian hayat berada dalam folder yang berasingan. Sekarang mari kita lihat struktur manifes yang kami terima

cat manifest.json | json_pp

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

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

Melaksanakan arahan docker pull dan docker push tanpa klien docker menggunakan permintaan HTTP

Jelas sekali, manifesto sedia ada tidak sesuai dengan kami, jadi kami akan membuat sendiri dengan blackjack dan courtesans, talian hayat dan konfigurasi.

Kami akan sentiasa mempunyai sekurang-kurangnya satu fail konfigurasi dan pelbagai talian hayat. Skim versi 2 (semasa pada masa penulisan), mediaType akan dibiarkan tidak 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

Selepas mencipta manifes asas, anda perlu mengisinya dengan data yang sah. Untuk melakukan ini, kami menggunakan templat json objek rel:

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

Kami akan menambahkannya pada manifes untuk setiap rel.

Seterusnya, kita perlu mengetahui saiz fail konfigurasi dan menggantikan stub dalam manifes dengan data sebenar

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

Kini anda boleh memulakan proses muat turun dan menyelamatkan diri anda uuid, yang sepatutnya mengiringi semua permintaan seterusnya.

Skrip lengkap kelihatan 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 boleh menggunakan skrip siap sedia:

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

UPD:
Apa yang kita dapat sebagai hasilnya?
Pertama, data sebenar untuk analisis, kerana ujian dijalankan dalam blazemeter dan data pada permintaan klien docker tidak begitu bermaklumat, tidak seperti permintaan HTTP tulen.

Kedua, peralihan itu membolehkan kami meningkatkan bilangan pengguna maya untuk muat naik buruh pelabuhan sebanyak kira-kira 150% dan mendapat purata masa tindak balas 20-25% lebih cepat. Untuk muat turun buruh pelabuhan, kami berjaya meningkatkan bilangan pengguna sebanyak 500%, manakala purata masa tindak balas menurun sebanyak kira-kira 60%.

Terima kasih atas perhatian anda.

Sumber: www.habr.com

Tambah komen