Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Volt 2 zsák fű, 75 mescaline tablet unix környezet, egy docker repository és a docker pull és docker push parancsok megvalósítása docker kliens nélkül.

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

UPS:
Kérdés: Minek ez az egész?
Válasz: A termék terhelési tesztelése (NEM használ bash-t, a szkriptek oktatási célokat szolgálnak). Úgy döntöttek, hogy nem használják a docker klienst további rétegek csökkentésére (ésszerű határokon belül), és ennek megfelelően nagyobb terhelés emulálására. Ennek eredményeként a Docker-ügyfél minden rendszerkésleltetése megszűnt. Közvetlenül a termékre viszonylag tiszta terhelést kaptunk.
A cikk az eszközök GNU verzióit használta.

Először is nézzük meg, mit csinálnak ezek a parancsok.

Tehát mire használják a docker pull-t? Alapján dokumentáció:

"Húzz le egy képet vagy egy adattárat a rendszerleíró adatbázisból".

Ott találunk egy linket is megérti a képeket, a tárolókat és a tároló-illesztőprogramokat.

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Innen érthetjük meg, hogy a docker kép bizonyos rétegek halmaza, amelyek a kép legfrissebb változásairól tartalmaznak információkat, amire nyilvánvalóan szükségünk van. Következő megnézzük regisztrációs API.

A következőt írja:

"A „kép” egy JSON-jegyzékfájl és az egyes rétegfájlok kombinációja. A kép előhívásának folyamata e két összetevő lekérésére irányul."

Tehát az első lépés a dokumentáció szerint "Kép Manifest húzása".

Természetesen nem fogjuk lelőni, de szükségünk van az adatokra. A következő példa egy kérés: GET /v2/{name}/manifests/{reference}

"A név és a referenciaparaméter azonosítja a képet, és kötelező. A hivatkozás tartalmazhat címkét vagy kivonatot."

Docker tárolónk helyben van telepítve, próbáljuk meg végrehajtani a kérést:

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 és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Válaszul json-t kapunk, amelyből jelenleg csak az életvonalak, vagy inkább azok hash-ei érdekelnek bennünket. Miután megkaptuk őket, mindegyiket végignézhetjük, és végrehajthatjuk a következő kérést: "GET /v2/{name}/blobs/{digest}"

"A réteghez való hozzáférést a lerakat neve korlátozza, de a rendszerleíró adatbázisban egyedileg azonosítja a kivonat."

digest ebben az esetben az a hash, amit kaptunk.

Megpróbálja

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 és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Lássuk, milyen fájlt kaptunk végre első mentőövként.

file firstLayer

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

azok. sínek tar archívumok, ezeket megfelelő sorrendben kicsomagolva megkapjuk a kép tartalmát.

Írjunk egy kis bash szkriptet, hogy mindez automatizálható legyen

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

Most már futtathatjuk a kívánt paraméterekkel, és megkapjuk a kívánt kép tartalmát

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

2. rész - Docker push

Ez egy kicsit bonyolultabb lesz.

Kezdjük újra azzal dokumentáció. Tehát le kell töltenünk minden vezetőt, össze kell gyűjtenünk a megfelelő manifestet, és azt is le kell töltenünk. Egyszerűnek tűnik.

A dokumentáció tanulmányozása után a letöltési folyamatot több lépésre oszthatjuk:

  • Folyamat inicializálása - "POST /v2/{repoName}/blobs/uploads/"
  • Mentőkötél feltöltése (monolit feltöltést fogunk használni, azaz minden mentőövet teljes egészében elküldünk) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Tartalom hossza: {réteg mérete}
    Tartalomtípus: alkalmazás/oktett-folyam
    Réteg bináris adat".
  • A jegyzék betöltése – „PUT /v2/{repoName}/manifests/{reference}”.

De a dokumentációból hiányzik egy lépés, amely nélkül semmi sem fog működni. Monolitikus, valamint részleges (darabolt) terhelés esetén a sín terhelése előtt egy PATCH kérést kell végrehajtania:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Tartalom hossza: {size of chunk}
Tartalomtípus: alkalmazás/oktett-folyam
{Layer Chunk Binary Data}".

Ellenkező esetben nem fog tudni túllépni az első ponton, mert... A várt 202-es válaszkód helyett 4xx-et fog kapni.

Az algoritmus most így néz ki:

  • Inicializálás
  • Patch sín
  • A kapaszkodó terhelése
  • A jegyzék betöltése
    A 2. és 3. pont annyiszor ismétlődik, ahány sort be kell tölteni.

Először is szükségünk van bármilyen képre. Az archlinux:latest programot fogom használni

docker pull archlinux

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Most mentsük el helyben további elemzés céljából

docker save c24fe13d37b9 -o savedArch

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Csomagolja ki a kapott archívumot az aktuális könyvtárba

tar xvf savedArch

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Amint látja, minden mentőkötél külön mappában van. Most nézzük meg a kapott manifeszt felépítését

cat manifest.json | json_pp

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Nem sok. Lássuk, milyen manifest szükséges a betöltéshez, szerint dokumentáció.

Docker pull és Docker push parancsok megvalósítása docker kliens nélkül HTTP kérések használatával

Nyilvánvaló, hogy a meglévő kiáltvány nem felel meg nekünk, ezért blackjack-kel és kurtizánokkal, mentőkötélekkel és konfigokkal elkészítjük a magunkét.

Mindig lesz legalább egy konfigurációs fájlunk és egy sor életvonalunk. A séma 2-es verziója (jelenleg az írás idején), a mediaType változatlan marad:

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

Az alapjegyzék létrehozása után ki kell töltenie érvényes adatokkal. Ehhez a rail objektum json sablonját használjuk:

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

Minden sínhez hozzáadjuk a manifesthez.

Ezután meg kell találnunk a konfigurációs fájl méretét, és a jegyzékben lévő csonkokat valós adatokra kell cserélnünk.

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

Most már elindíthatja a letöltési folyamatot, és menthet magának egy uuid-t, amely minden további kérést kísér.

A teljes szkript valahogy így néz ki:

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

használhatunk egy kész szkriptet:

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

UPS:
Mit kaptunk ennek eredményeként?
Először is valós adatok az elemzéshez, mivel a tesztek blazemeterben futnak, és a docker kliens kérések adatai nem túl informatívak, ellentétben a tiszta HTTP kérésekkel.

Másodszor, az átállás lehetővé tette számunkra, hogy körülbelül 150%-kal növeljük a docker feltöltéshez szükséges virtuális felhasználók számát, és 20-25%-kal gyorsabban tudtuk elérni az átlagos válaszidőt. A docker letöltésnél 500%-kal sikerült növelnünk a felhasználók számát, miközben az átlagos válaszidő körülbelül 60%-kal csökkent.

Köszönöm a figyelmet.

Forrás: will.com

Hozzászólás