HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

2 torba çim, 75 meskalin tablet unix ortamı, bir docker deposu ve docker istemcisi olmadan docker pull ve docker push komutlarını uygulama görevimiz vardı.

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

UPD:
Soru: Bütün bunlar ne için?
Cevap: Ürünün yük testi (bash KULLANILMAZ, komut dosyaları eğitim amaçlıdır). Ek katmanları azaltmak (makul sınırlar dahilinde) ve buna bağlı olarak daha yüksek bir yükü taklit etmek için docker istemcisinin kullanılmamasına karar verildi. Sonuç olarak Docker istemcisinin tüm sistem gecikmeleri ortadan kaldırıldı. Doğrudan ürün üzerinde nispeten temiz bir yük aldık.
Makalede araçların GNU sürümleri kullanıldı.

Öncelikle bu komutların ne işe yaradığını bulalım.

Peki docker pull ne için kullanılır? Buna göre belgeleme:

"Kayıt defterinden bir resim veya depo çekin".

Orada ayrıca bir bağlantı buluyoruz görüntüleri, kapsayıcıları ve depolama sürücülerini anlama.

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Buradan docker imajının, imajdaki en son değişiklikler hakkında bilgi içeren belirli katmanlar kümesi olduğunu anlayabiliriz ki buna da ihtiyacımız var. Sonra bakıyoruz kayıt defteri API'si.

Aşağıdakileri söylüyor:

"Bir "görüntü", bir JSON manifest dosyası ile ayrı katman dosyalarının birleşimidir. Bir görüntü çekme işlemi, bu iki bileşenin alınmasına odaklanır."

Yani belgelere göre ilk adım “Görüntü Bildirimini Çekme anlayışının sonucu olarak, buzdolabında iki üç günden fazla durmayan küçük şişeler elinizin altında bulunur.

Elbette onu vurmayacağız ama onun verilerine ihtiyacımız var. Aşağıda örnek bir istek verilmiştir: GET /v2/{name}/manifests/{reference}

"Ad ve referans parametresi görüntüyü tanımlar ve gereklidir. Referans bir etiket veya özet içerebilir."

Liman işçisi depomuz yerel olarak konuşlandırıldı, isteği yürütmeyi deneyelim:

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

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Yanıt olarak, şu anda yalnızca yaşam çizgileriyle veya daha doğrusu karmalarıyla ilgilendiğimiz json'u alıyoruz. Bunları aldıktan sonra her birini inceleyebilir ve şu isteği yürütebiliriz: "GET /v2/{name}/blobs/{digest}"

"Bir katmana erişim, deponun adıyla kapatılacak ancak kayıt defterinde özet ile benzersiz bir şekilde tanımlanacak."

bu durumda özet, aldığımız karmadır.

denemek

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

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Bakalım nihayet ilk cankurtaran halatı olarak nasıl bir dosya aldık.

file firstLayer

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

onlar. raylar katran arşivleridir, bunları uygun sırayla açarak görüntünün içeriğini alacağız.

Tüm bunların otomatikleştirilebilmesi için küçük bir bash betiği yazalım

#!/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

Artık onu istenilen parametrelerle çalıştırabilir ve gerekli görüntünün içeriğini alabiliriz.

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

Bölüm 2 - Docker Push

Bu biraz daha karmaşık olacak.

Tekrar başlayalım belgeleme. Bu yüzden her lideri indirmemiz, ilgili manifestoyu toplamamız ve onu da indirmemiz gerekiyor. Basit görünüyor.

Belgeleri inceledikten sonra indirme işlemini birkaç adıma ayırabiliriz:

  • İşlem başlatma - "POST /v2/{repoName}/blobs/uploads/"
  • Bir yaşam çizgisi yükleme (yekpare bir yükleme kullanacağız, yani her yaşam çizgisini bütünüyle göndereceğiz) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    İçerik Uzunluğu: {katmanın boyutu}
    İçerik Türü: uygulama/sekizli akış
    İkili Veri Katmanı".
  • Manifest yükleniyor - "PUT /v2/{repoName}/manifests/{reference}".

Ancak dokümantasyon bir adımı atlıyor ve o olmadan hiçbir şey işe yaramayacak. Monolitik yüklemenin yanı sıra kısmi (parçalı) yükleme için rayı yüklemeden önce bir PATCH isteği gerçekleştirmelisiniz:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
İçerik Uzunluğu: {yığın boyutu}
İçerik Türü: uygulama/sekizli akış
{Katman Parça İkili Verileri}".

Aksi takdirde ilk noktanın ötesine geçemezsiniz çünkü... Beklenen yanıt kodu 202 yerine 4xx alacaksınız.

Artık algoritma şuna benziyor:

  • Başlatma
  • Yama rayı
  • Küpeştenin yüklenmesi
  • Manifest yükleniyor
    Sırasıyla 2 ve 3 numaralı noktalar, yüklenmesi gereken satır sayısı kadar tekrarlanacaktır.

Öncelikle herhangi bir görsele ihtiyacımız var. Archlinux'u kullanacağım: en son

docker pull archlinux

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Şimdi daha fazla analiz için yerel olarak kaydedelim

docker save c24fe13d37b9 -o savedArch

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Ortaya çıkan arşivi mevcut dizine açın

tar xvf savedArch

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Gördüğünüz gibi her yaşam çizgisi ayrı bir klasördedir. Şimdi aldığımız manifestonun yapısına bakalım

cat manifest.json | json_pp

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Fazla değil. Bakalım yüklemek için hangi manifest'in gerekli olduğunu görelim. belgeleme.

HTTP isteklerini kullanarak docker istemcisi olmadan docker pull ve docker push komutlarını uygulama

Açıkçası, mevcut manifesto bize uymuyor, bu yüzden blackjack ve fahişeler, yaşam çizgileri ve yapılandırmalarla kendimizinkini yapacağız.

Her zaman en az bir yapılandırma dosyamız ve bir dizi yaşam çizgimiz olacaktır. Şema sürüm 2 (yazma sırasında geçerli), mediaType değişmeden bırakılacaktır:

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

Temel bildirimi oluşturduktan sonra onu geçerli verilerle doldurmanız gerekir. Bunu yapmak için ray nesnesinin json şablonunu kullanıyoruz:

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

Bunu her ray için manifest dosyasına ekleyeceğiz.

Daha sonra, yapılandırma dosyasının boyutunu bulmamız ve bildirimdeki taslakları gerçek verilerle değiştirmemiz gerekiyor.

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

Artık indirme işlemini başlatabilir ve kendinize sonraki tüm isteklere eşlik etmesi gereken bir kullanıcı kimliği kaydedebilirsiniz.

Komut dosyasının tamamı şuna benzer:

#!/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

hazır bir komut dosyası kullanabiliriz:

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

UPD:
Sonuç olarak ne elde ettik?
İlk olarak, analiz için gerçek veriler, çünkü testler blazemeter'da yürütülüyor ve liman işçisi istemci isteklerindeki veriler, saf HTTP isteklerinin aksine çok bilgilendirici değil.

İkinci olarak, geçiş, liman işçisi yüklemesi için sanal kullanıcı sayısını yaklaşık %150 artırmamıza ve ortalama yanıt süresini %20-25 daha hızlı almamıza olanak sağladı. Docker indirmede kullanıcı sayısını %500 artırmayı başardık, ortalama yanıt süresini ise yaklaşık %60 oranında düşürdük.

İlginiz için teşekkür ederiz.

Kaynak: habr.com

Yorum ekle