Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Turėjome 2 maišus žolės, 75 mescaline tablečių unix aplinką, docker saugyklą ir užduotį įgyvendinti docker pull ir docker push komandas be docker kliento.

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

UPD:
Klausimas: Kam visa tai?
Atsakymas: Produkto apkrovos testavimas (NEnaudojamas bash, scenarijai pateikiami edukaciniais tikslais). Buvo nuspręsta nenaudoti docker kliento norint sumažinti papildomus sluoksnius (protingose ​​​​ribose) ir atitinkamai emuliuoti didesnę apkrovą. Dėl to buvo pašalinti visi „Docker“ kliento sistemos vėlavimai. Tiesiogiai ant gaminio gavome gana švarią apkrovą.
Straipsnyje buvo naudojamos GNU įrankių versijos.

Pirmiausia išsiaiškinkime, ką daro šios komandos.

Taigi kam naudojamas docker pull? Pagal dokumentacija:

„Ištraukite vaizdą arba saugyklą iš registro“.

Ten taip pat randame nuorodą į suprasti vaizdus, ​​konteinerius ir saugojimo tvarkykles.

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Iš čia galime suprasti, kad docker vaizdas yra tam tikrų sluoksnių rinkinys, kuriame yra informacija apie naujausius vaizdo pakeitimus, o tai, be abejo, yra tai, ko mums reikia. Toliau žiūrime registro API.

Jame rašoma taip:

„Vaizdas“ yra JSON aprašo ir atskirų sluoksnių failų derinys. Vaizdo > ištraukimo procesas yra susijęs su šių dviejų komponentų gavimu.

Taigi pirmasis žingsnis pagal dokumentaciją yra „Vaizdo manifesto traukimas".

Žinoma, mes to nešaudysime, bet mums reikia duomenų iš jo. Toliau pateikiamas prašymo pavyzdys: GET /v2/{name}/manifests/{reference}

"Pavadinimas ir nuorodos parametras identifikuoja vaizdą ir yra būtini. Nuorodoje gali būti žyma arba santrauka."

Mūsų dokerių saugykla yra įdiegta vietoje, pabandykime įvykdyti užklausą:

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

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Atsakydami gauname json, iš kurio šiuo metu mus domina tik gelbėjimo linijos, tiksliau, jų maišos. Gavę juos, galime peržiūrėti kiekvieną ir įvykdyti šią užklausą: „GET /v2/{name}/blobs/{digest}“

„Prieiga prie sluoksnio bus apribota pagal saugyklos pavadinimą, tačiau registre ji unikaliai identifikuojama pagal santrauką.

„Digest“ šiuo atveju yra maiša, kurią gavome.

Pabandykime

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

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Pažiūrėkime, kokį failą galiausiai gavome kaip pirmąjį gelbėjimosi ratą.

file firstLayer

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

tie. bėgiai yra tar archyvai, juos išpakavus atitinkama tvarka gausime vaizdo turinį.

Parašykime nedidelį bash scenarijų, kad visa tai būtų galima automatizuoti

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

Dabar galime jį paleisti su norimais parametrais ir gauti reikiamo vaizdo turinį

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

2 dalis – dokerio stūmimas

Tai bus šiek tiek sudėtingesnė.

Pradėkime iš naujo nuo dokumentacija. Taigi turime atsisiųsti kiekvieną lyderį, surinkti atitinkamą manifestą ir jį atsisiųsti. Atrodo paprasta.

Išstudijavę dokumentaciją, atsisiuntimo procesą galime padalyti į kelis etapus:

  • Proceso inicijavimas – „POST /v2/{repoName}/blobs/uploads/“
  • Gelbėjimo juostos įkėlimas (naudosime monolitinį įkėlimą, t. y. siunčiame visą gelbėjimo liniją) – „PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Turinio ilgis: {sluoksnio dydis}
    Turinio tipas: programa / okteto srautas
    Dvejetainių duomenų sluoksnis“.
  • Įkeliamas aprašas – „PUT /v2/{repoName}/manifests/{reference}“.

Tačiau dokumentacija praleidžia vieną žingsnį, be kurio niekas neveiks. Monolitiniam pakrovimui, taip pat daliniam (smulkintam), prieš pakraunant bėgį, turite atlikti PATCH užklausą:

PATCH /v2/{repoName}/blobs/uploads/{uuid}
Turinio ilgis: {size of chunk}
Turinio tipas: programa / okteto srautas
{Layer Cunk Binary Data}".

Priešingu atveju negalėsite peržengti pirmojo taško, nes... Vietoj laukiamo atsakymo kodo 202 gausite 4xx.

Dabar algoritmas atrodo taip:

  • Inicijavimas
  • Patch bėgelis
  • Turėklo pakrovimas
  • Įkeliamas manifestas
    Atitinkamai 2 ir 3 taškai bus kartojami tiek kartų, kiek reikės įkelti eilučių.

Pirma, mums reikia bet kokio vaizdo. Naudosiu archlinux:latest

docker pull archlinux

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Dabar išsaugokime jį vietoje tolesnei analizei

docker save c24fe13d37b9 -o savedArch

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Išpakuokite gautą archyvą į dabartinį katalogą

tar xvf savedArch

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Kaip matote, kiekviena gelbėjimo linija yra atskirame aplanke. Dabar pažiūrėkime į gauto manifesto struktūrą

cat manifest.json | json_pp

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Nedaug. Pažiūrėkime, pagal kurį manifestą reikia įkelti dokumentacija.

Docker pull ir Docker push komandų įgyvendinimas be docker kliento naudojant HTTP užklausas

Akivaizdu, kad esamas manifestas mums netinka, todėl sukursime savo su blackjack ir kurtizanėmis, gelbėjimosi linijomis ir konfigūracijomis.

Mes visada turėsime bent vieną konfigūracijos failą ir daugybę gelbėjimo linijų. 2 schemos versija (dabartinė rašymo metu), mediaType bus palikta nepakeista:

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

Sukūrę pagrindinį manifestą, turite jį užpildyti tinkamais duomenimis. Norėdami tai padaryti, naudojame bėgio objekto json šabloną:

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

Pridėsime jį prie kiekvieno bėgio manifesto.

Tada turime išsiaiškinti konfigūracijos failo dydį ir pakeisti aprašo šaknis tikrais duomenimis

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

Dabar galite pradėti atsisiuntimo procesą ir išsaugoti uuid, kuris turėtų būti pridedamas prie visų vėlesnių užklausų.

Visas scenarijus atrodo maždaug taip:

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

galime naudoti paruoštą scenarijų:

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

UPD:
Ką mes gavome dėl to?
Pirma, tikri duomenys analizei, nes testai vykdomi blazemeter, o duomenys apie docker kliento užklausas nėra labai informatyvūs, skirtingai nuo grynų HTTP užklausų.

Antra, perėjimas leido apie 150% padidinti virtualių naudotojų skaičių įkeliant dokerį ir gauti vidutinį atsako laiką 20–25% greičiau. „Docker“ atsisiuntimo atveju mums pavyko padidinti vartotojų skaičių 500%, o vidutinis atsako laikas sumažėjo apie 60%.

Ačiu už dėmesį.

Šaltinis: www.habr.com

Добавить комментарий