Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Meillä oli 2 pussillista ruohoa, 75 meskaliinitablettia unix-ympäristö, docker-varasto ja tehtävänä toteuttaa telakointiaseman veto- ja työntökomennot ilman docker-asiakasta.

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

UPD:
Kysymys: Mitä varten tämä kaikki on?
Vastaus: Tuotteen kuormitustestaus (EI käyttämällä bashia, skriptit toimitetaan opetustarkoituksiin). Docker-asiakasta päätettiin olla käyttämättä lisäkerrosten vähentämiseen (kohtuullisissa rajoissa) ja vastaavasti suuremman kuormituksen emulointiin. Tämän seurauksena kaikki Docker-asiakkaan järjestelmäviiveet poistettiin. Saimme suhteellisen puhtaan kuorman suoraan tuotteeseen.
Artikkelissa käytettiin työkalujen GNU-versioita.

Selvitetään ensin, mitä nämä komennot tekevät.

Joten mihin docker pullia käytetään? Mukaan dokumentointi:

"Vedä kuva tai arkisto rekisteristä".

Sieltä löytyy myös linkki ymmärtää kuvia, säilöjä ja tallennusajureita.

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Tästä voimme ymmärtää, että Docker-kuva on joukko tiettyjä kerroksia, jotka sisältävät tietoa viimeisimmistä muutoksista kuvassa, mitä tietysti tarvitsemme. Seuraavaksi katsomme rekisterin API.

Siinä lukee seuraavaa:

""Kuva" on yhdistelmä JSON-luettelosta ja yksittäisistä tasotiedostoista. Kuvan vetäminen keskittyy näiden kahden komponentin hakemiseen."

Joten ensimmäinen askel dokumentaation mukaan on "Kuvamanifestin vetäminen".

Emme tietenkään ammu sitä, mutta tarvitsemme sen tiedot. Seuraava on esimerkkipyyntö: GET /v2/{name}/manifests/{reference}

"Nimi ja viiteparametri tunnistavat kuvan ja ovat pakollisia. Viite voi sisältää tunnisteen tai tiivistelmän."

Docker-varastomme on otettu käyttöön paikallisesti, yritetään suorittaa pyyntö:

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- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Vastauksena saamme jsonin, josta olemme tällä hetkellä kiinnostuneita vain elinlinjoista tai pikemminkin niiden tiivisteistä. Saatuamme ne, voimme käydä jokaisen läpi ja suorittaa seuraavan pyynnön: "GET /v2/{name}/blobs/{digest}"

"Pääsy tasoon suojataan arkiston nimellä, mutta se tunnistetaan yksilöllisesti rekisterissä tiivistelmällä."

Digest on tässä tapauksessa saamamme hash.

Yritetään

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- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Katsotaan millaisen tiedoston lopulta saimme ensimmäiseksi pelastusköydeksi.

file firstLayer

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

nuo. kiskot ovat terva-arkistoja, purkamalla ne sopivassa järjestyksessä saamme kuvan sisällön.

Kirjoitetaan pieni bash-skripti, jotta kaikki tämä voidaan automatisoida

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

Nyt voimme ajaa sen halutuilla parametreilla ja saada tarvittavan kuvan sisällön

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

Osa 2 - telakkatyöntö

Tämä tulee olemaan hieman monimutkaisempi.

Aloitetaan uudestaan dokumentointi. Joten meidän on ladattava jokainen johtaja, kerättävä vastaava manifesti ja ladattava se. Se näyttää yksinkertaiselta.

Dokumentaation tutkimisen jälkeen voimme jakaa latausprosessin useisiin vaiheisiin:

  • Prosessin alustus - "POST /v2/{repoName}/blobs/uploads/"
  • Pelastusköyden lataaminen (käytämme monoliittista latausta, eli lähetämme jokaisen pelastusköyden kokonaisuudessaan) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Sisällön pituus: {kerroksen koko}
    Sisältötyyppi: sovellus/oktettivirta
    Layer Binary Data".
  • Ladataan luetteloa - "PUT /v2/{repoName}/manifests/{reference}".

Mutta dokumentaatiosta puuttuu yksi vaihe, jota ilman mikään ei toimi. Monoliittinen kuorma, samoin kuin osittainen (palastettu), ennen kiskon lastausta on suoritettava PATCH-pyyntö:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Sisällön pituus: {size of chunk}
Sisältötyyppi: sovellus/oktettivirta
{Layer Chunk Binary Data}".

Muuten et voi siirtyä ensimmäisen pisteen pidemmälle, koska... Odotetun vastauskoodin 202 sijasta saat 4xx.

Nyt algoritmi näyttää tältä:

  • Alustus
  • Patch kisko
  • Kaiteen lastaus
  • Ladataan luetteloa
    Kohdat 2 ja 3 toistetaan niin monta kertaa kuin rivejä on ladattava.

Ensinnäkin tarvitsemme minkä tahansa kuvan. Käytän archlinux:latest

docker pull archlinux

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Tallennetaan se nyt paikallisesti lisäanalyysiä varten

docker save c24fe13d37b9 -o savedArch

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Pura tuloksena oleva arkisto nykyiseen hakemistoon

tar xvf savedArch

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Kuten näet, jokainen pelastusköysi on erillisessä kansiossa. Katsotaanpa nyt saamamme manifestin rakennetta

cat manifest.json | json_pp

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Ei paljon. Katsotaan, mikä manifesti on ladattava, sen mukaan dokumentointi.

Docker pull- ja Docker push -komentojen toteuttaminen ilman Docker-asiakasta HTTP-pyyntöjen avulla

Ilmeisesti olemassa oleva manifesti ei sovi meille, joten teemme omamme blackjackilla ja kurtisaaneilla, pelastuslinjoilla ja asetuksilla.

Meillä on aina vähintään yksi asetustiedosto ja joukko elinlinjoja. Kaavan versio 2 (nykyinen kirjoitushetkellä), mediaType jätetään ennalleen:

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

Perusluettelon luomisen jälkeen sinun on täytettävä se kelvollisilla tiedoilla. Käytämme tähän rail-objektin json-mallia:

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

Lisäämme sen jokaisen kiskon manifestiin.

Seuraavaksi meidän on selvitettävä konfigurointitiedoston koko ja korvattava luettelon tyngät todellisilla tiedoilla

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

Nyt voit aloittaa latausprosessin ja tallentaa itsellesi uuid-tunnuksen, jonka pitäisi liittää kaikkiin myöhempiin pyyntöihin.

Koko skripti näyttää suunnilleen tältä:

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

voimme käyttää valmiita skriptiä:

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

UPD:
Mitä saimme tuloksena?
Ensinnäkin todellista dataa analysoitavaksi, koska testit ajetaan blazemeterissä ja docker-asiakaspyyntöjen tiedot eivät ole kovin informatiivisia, toisin kuin puhtaat HTTP-pyynnöt.

Toiseksi siirron ansiosta pystyimme lisäämään virtuaalisten käyttäjien määrää docker-latauksessa noin 150 % ja saada keskimääräinen vasteaika 20-25 % nopeammaksi. Docker-latauksen osalta onnistuimme kasvattamaan käyttäjien määrää 500 %, kun taas keskimääräinen vasteaika lyheni noin 60 %.

Kiitos huomiota.

Lähde: will.com

Lisää kommentti