Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Meil oli 2 kotitäit rohtu, 75 meskaliinitabletti, unix keskkond, docker hoidla ning ülesanne teostada komandad "docker pull" ja "docker push" ilma docker kliendita.

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

UPD:
Küsimus: Miks see kõik on vajalik?
Vastus: Toote koormustestimine (Mitte bash'i abil, skriptid on antud hariduslikel eesmärkidel). Otsustasime mitte kasutada docker klienti, et vähendada lisakihti (mõistlikes piirides) ja seega simuleerida kõrgemat koormust. Tulemusena kõrvaldasime kõik süsteemilised viivitused docker kliendist. Saime suhteliselt puhta koormuse otse tootele.
Artiklis kasutati GNU versioone.

Alustame sellega, mida need käsud teevad.

Nii et milleks kasutatakse docker pull? Vastavalt dokumentatsioonis:

"Laadi pilt või hoidla registrist alla."

Sealt leiame viite mõista pilte, konteinerite ja salvestusjuhte..

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Siit saame aru, et docker pilt on teatud kihtide kogum, mis sisaldavad teavet viimaste muudatuste kohta pildis, mis on ilmselgelt vajalikud. Jätkame vaadates registri API..

Siin öeldakse järgmist:

"Pilt" on JSON-manifesti ja eraldi kihtide failide kombinatsioon. Pilti tõmbamise protsess keskendub nendele kahhele komponendile.

Nii et esimene samm vastavalt dokumentatsioonile on “Pildi manifesti tõmbamine”.

Me ei hakka seda tõmbama, kuid andmed sealt on meile vajalikud. Järgnevalt on toodud näide päringust: GET /v2/{name}/manifests/{reference}

"Nime ja viitamise parameeter tuvastavad pildi ning on vajalikud. Viide võib sisaldada sildi või digest'i."

Meie Docker'i registripood on kohapeal üles seatud, proovime teha päringut:

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

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Vastuseks saame JSON'i, millest meid huvitavad praegu ainult kihid, täpsemalt nende hash'id. Saades need, saame igaühe kohta teostada järgmise päringu: "GET /v2/{name}/blobs/{digest}"

Kihile pääs juurutatakse registri nime kaudu, kuid see tuvastatakse registris unikaalselt hash'i abil.

Hash on antud juhul just see hash, mille saime.

Proovime

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 komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Vaadakem, milline fail meil lõpuks esimese kihina on.

file firstLayer

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

st. kihid on tar-arhiivid, mille avamisel õiges järjekorras saame pildi sisu.

Kirjutame väikese bash-skripti, et seda kõike automatiseerida.

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

Nüüd saame selle käivitada soovitud parameetritega ja saada vajaliku pildi sisu.

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

Osa 2 — docker push

Siin saab olema veidi keerulisem.

Alustame uuesti. dokumentatsioonisSeega peame üles laadima iga kihistuse, koguma vastava manifesti ja laadima selle samuti üles. Tundub lihtne.

Dokumentatsiooni uurides saame jagada üleslaadimisprotsessi mitmeks sammuks:

  • Protsessi initsialiseerimine — "POST /v2/{repoName}/blobs/uploads/"
  • Kihistuse üleslaadimine (kasutame monoliitset üleslaadimist, see tähendab, et iga kihistus saadetakse täielikult) — "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Content-Length: {size of layer}
    Content-Type: application/octet-stream
    Kihistu binaarandmed".
  • Manifesti üleslaadimine — "PUT /v2/{repoName}/manifests/{reference}".

Kuid dokumentatsioonist on üks samm puudu, ilma milleta ei õnnestu. Monoliitset üleslaadimist, nagu ka osalist (chunked), eelnevalt eelnevalt обувку laadimise eest, tuleb teha PATCH-päring:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Content-Length: {size of chunk}
Content-Type: application/octet-stream
{Layer Chunk Binary Data}".

Vastasel juhul ei saa te edasi liikuda esimesest punktist, kuna ootate vastuskoodi 202, aga saate 4xx.

Nüüd näeb algoritm välja järgmine:

  • Initsialiseerimine
  • Kihi patch
  • Kihi laadimine
  • Manifesti laadimine
    Punktid 2 ja 3 korduvad vastavalt nii mitu korda, kui on kihte, mida laadida.

Alustuseks vajame ühte pilti. Ma kasutan archlinux:latest.

docker pull archlinux

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Nüüd salvestame selle kohaliku analüüsi jaoks.

docker save c24fe13d37b9 -o savedArch

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Väljastame saadud arhiivi praegusesse katalooge.

tar xvf savedArch

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Nagu näeme, asub iga kiht eraldi kaustas. Vaadakem nüüd manifesti struktuuri, mille saime.

cat manifest.json | json_pp

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Ei just sugugi. Vaadakem, milline manifest on laadimiseks vajalik, vastavalt. dokumentatsioonis.

Docker komandad "pull" ja "push" teostamine ilma docker kliendita HTTP päringute kaudu.

Selgelt ei sobi olemasolev manifest meile, seega teeme oma, mustade mängude ja kurtisanide kihtide ning konfiguratsioonidega.

Meil on alati vähemalt üks config-fail ja massiiv kihte. Skeemi versioon 2 (kehtiv artikli kirjutamise ajal), mediaType jätame muutmata:

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

Pärast põhimanifesti loomist tuleb see täita kehtivate andmetega. Selleks kasutame kihil JSON objekti malli:

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

seda lisame igasse kihti manifestis.

Seejärel peame välja selgitama konfigureerimisfaili suuruse ja asendama malli kohad tegelike andmetega.

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

Nüüd saab alustada laadimisprotsessi ja salvestada endale uuid, millega peavad kõik järgnevad päringud kaasas olema.

Täielik skript näeb välja umbes selline:

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

saame kasutada valmis skripti:

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

UPD:
Mida me tulemuseks saime?
Esiteks, reaalsed andmed analüüsiks, kuna testid käivitatakse blazemeter'is ja Docker kliendi päringute andmed on üsna ebatäpsed erinevalt puhastest HTTP päringutest.

Teiseks, üleminek võimaldas meil suurendada virtuaalsete kasutajate arvu docker upload'ile umbes 150% ja samal ajal saavutada keskmise vastuseaja 20-25% kiiremini. Docker download'i puhul õnnestus kasutajate arvu suurendada 500%, keskmine vastuseaeg langes samal ajal umbes 60% võrra.

Aitäh tähelepanu eest.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster