Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Imeli smo 2 vrečki trave, 75 tablet meskalina unix okolje, docker repozitorij in nalogo izvajanja ukazov docker pull in docker push brez docker klienta.

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

UPS:
Vprašanje: Čemu je vse to namenjeno?
Odgovor: Obremenitveno testiranje izdelka (NE uporablja bash, skripte so na voljo v izobraževalne namene). Odločeno je bilo, da se odjemalec docker ne uporablja za zmanjšanje dodatnih plasti (v razumnih mejah) in v skladu s tem posnema večjo obremenitev. Posledično so bile odstranjene vse sistemske zamude odjemalca Docker. Prejeli smo relativno čisto obremenitev neposredno na izdelek.
V članku so bile uporabljene različice orodij GNU.

Najprej ugotovimo, kaj počnejo ti ukazi.

Za kaj se torej uporablja docker pull? Po navedbah dokumentacijo:

"Potegnite sliko ali repozitorij iz registra".

Tam najdemo tudi povezavo do razumeti slike, vsebnike in gonilnike za shranjevanje.

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Od tu lahko razumemo, da je docker slika nabor določenih plasti, ki vsebujejo informacije o zadnjih spremembah slike, kar je očitno tisto, kar potrebujemo. Naprej pogledamo registrski API.

Piše naslednje:

"»Slika« je kombinacija manifesta JSON in posameznih datotek plasti. Postopek vlečenja > slike je osredotočen na pridobivanje teh dveh komponent.«

Prvi korak v skladu z dokumentacijo je torej "Vlečenje slikovnega manifesta".

Seveda ga ne bomo posneli, vendar potrebujemo podatke iz njega. Sledi primer zahteve: GET /v2/{name}/manifests/{reference}

"Ime in referenčni parameter identificirata sliko in sta obvezna. Referenca lahko vključuje oznako ali povzetek."

Naše skladišče dockerjev je nameščeno lokalno, poskusimo izvesti zahtevo:

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

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

V odgovor prejmemo json, od katerega nas trenutno zanimajo samo rešilne vrvice oziroma njihovi hashi. Ko jih prejmemo, lahko pregledamo vsakega posebej in izvedemo naslednjo zahtevo: "GET /v2/{name}/blobs/{digest}"

"Dostop do plasti bo omejen z imenom repozitorija, vendar je v registru edinstveno identificiran s prebavo."

digest v tem primeru je hash, ki smo ga prejeli.

Poskušam

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

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Poglejmo, kakšno datoteko smo na koncu prejeli kot prvo rešilno bilko.

file firstLayer

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

tiste. rails so tar arhivi, če jih razpakiramo v ustreznem vrstnem redu, bomo dobili vsebino slike.

Napišimo majhen bash skript, da bo vse to mogoče avtomatizirati

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

Zdaj ga lahko zaženemo z želenimi parametri in pridobimo vsebino zahtevane slike

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

2. del – docker push

To bo malo bolj zapleteno.

Začnimo znova z dokumentacijo. Zato moramo prenesti vsakega voditelja, zbrati ustrezni manifest in ga tudi prenesti. Zdi se preprosto.

Po preučitvi dokumentacije lahko postopek prenosa razdelimo na več korakov:

  • Inicializacija procesa - "POST /v2/{repoName}/blobs/uploads/"
  • Nalaganje rešilne vrvice (uporabili bomo monolitno nalaganje, tj. poslali bomo vsako rešilno vrvico v celoti) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Dolžina vsebine: {size of layer}
    Content-Type: aplikacija/toket-tok
    Sloj binarnih podatkov".
  • Nalaganje manifesta - "PUT /v2/{repoName}/manifests/{reference}".

Dokumentaciji pa manjka en korak, brez katerega ne bo šlo nič. Za monolitno nalaganje, pa tudi za delno (v kosih), morate pred nalaganjem tirnice izvesti zahtevo PATCH:

"POPIS /v2/{repoName}/blobs/uploads/{uuid}
Dolžina vsebine: {size of chunk}
Content-Type: aplikacija/toket-tok
{Layer Chunk Binary Data}".

V nasprotnem primeru se ne boste mogli premakniti dlje od prve točke, ker ... Namesto pričakovane kode odgovora 202 boste prejeli 4xx.

Zdaj algoritem izgleda takole:

  • Inicializacija
  • Patch tirnica
  • Nalaganje ograje
  • Nalaganje manifesta
    Točki 2 oziroma 3 se bosta ponovili tolikokrat, kolikor vrstic je treba naložiti.

Najprej potrebujemo katero koli sliko. Uporabil bom archlinux:najnovejši

docker pull archlinux

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Zdaj pa ga shranimo lokalno za nadaljnjo analizo

docker save c24fe13d37b9 -o savedArch

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Razpakirajte nastali arhiv v trenutni imenik

tar xvf savedArch

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Kot lahko vidite, je vsaka rešilna vrv v ločeni mapi. Zdaj pa poglejmo strukturo manifesta, ki smo ga prejeli

cat manifest.json | json_pp

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Ni veliko. Poglejmo, kateri manifest je potreben za nalaganje glede na dokumentacijo.

Izvajanje ukazov docker pull in docker push brez odjemalca docker z uporabo zahtev HTTP

Očitno nam obstoječi manifest ne ustreza, zato bomo naredili svojega z blackjackom in kurtizani, rešilnimi vrvmi in konfiguracijami.

Vedno bomo imeli vsaj eno konfiguracijsko datoteko in niz rešilnih vrvi. Različica sheme 2 (trenutna v času pisanja), mediaType bo ostal nespremenjen:

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

Ko ustvarite osnovni manifest, ga morate izpolniti z veljavnimi podatki. Za to uporabimo predlogo json objekta rail:

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

Dodali ga bomo v manifest za vsako tirnico.

Nato moramo ugotoviti velikost konfiguracijske datoteke in zamenjati škrbine v manifestu z resničnimi podatki

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

Zdaj lahko začnete postopek prenosa in si shranite uuid, ki bi moral spremljati vse nadaljnje zahteve.

Celoten skript je videti nekako takole:

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

lahko uporabimo že pripravljen skript:

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

UPS:
Kaj smo dobili kot rezultat?
Prvič, pravi podatki za analizo, saj se testi izvajajo v blazemetru in podatki o zahtevah odjemalca dockerja niso zelo informativni, za razliko od čistih zahtev HTTP.

Drugič, prehod nam je omogočil, da smo povečali število virtualnih uporabnikov za nalaganje dockerja za približno 150 % in dobili povprečni odzivni čas za 20–25 % hitreje. Za prenos docker nam je uspelo povečati število uporabnikov za 500 %, medtem ko se je povprečni odzivni čas zmanjšal za približno 60 %.

Hvala za vašo pozornost.

Vir: www.habr.com

Dodaj komentar