Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Ni havis 2 sakojn da herbo, 75 meskalinajn tabelojn uniksan medion, docker-deponejon kaj la taskon efektivigi la docker-tiro kaj docker-puŝkomandojn sen docker-kliento.

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

UPS:
Demando: Por kio ĉio ĉi?
Respondo: Ŝarĝu testadon de la produkto (NE uzante bash, la skriptoj estas provizitaj por edukaj celoj). Estis decidite ne uzi la docker-klienton por redukti pliajn tavolojn (ene de raciaj limoj) kaj, sekve, emuli pli altan ŝarĝon. Kiel rezulto, ĉiuj sistemaj prokrastoj de la Docker-kliento estis forigitaj. Ni ricevis relative puran ŝarĝon rekte sur la produkto.
La artikolo uzis GNU-versiojn de iloj.

Unue, ni eltrovu, kion faras ĉi tiuj komandoj.

Do por kio estas docker pull uzata? Laŭ dokumentado:

"Tiru bildon aŭ deponejon el registro".

Tie ni trovas ankaŭ ligilon al komprenu bildojn, ujojn kaj stokajn ŝoforojn.

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

De ĉi tie ni povas kompreni, ke docker-bildo estas aro de certaj tavoloj, kiuj enhavas informojn pri la lastaj ŝanĝoj en la bildo, kio evidente estas tio, kion ni bezonas. Poste ni rigardas registra API.

Ĝi diras la jenon:

""Bildo" estas kombinaĵo de JSON-manifesto kaj individuaj tavoldosieroj. La procezo de tiri > bildon centras pri reakiro de ĉi tiuj du komponantoj."

Do la unua paŝo laŭ la dokumentado estas "Tirante Bildo-Manifeston".

Kompreneble, ni ne pafos ĝin, sed ni bezonas la datumojn de ĝi. Jen ekzemplo peto: GET /v2/{name}/manifests/{reference}

"La nomo kaj referenca parametro identigas la bildon kaj estas postulataj. La referenco povas inkluzivi etikedon aŭ digeston."

Nia docker-deponejo estas deplojita loke, ni provu plenumi la peton:

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

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Responde, ni ricevas json, de kiu ni nuntempe nur interesiĝas pri la vivlinioj, aŭ pli ĝuste iliaj haŝoj. Ricevinte ilin, ni povas trarigardi ĉiun kaj plenumi la jenan peton: "GET /v2/{name}/blobs/{digest}"

"Aliro al tavolo estos barita per la nomo de la deponejo sed estas identigita unike en la registro per digesto."

digest en ĉi tiu kazo estas la haŝiŝo kiun ni ricevis.

Provante

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

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Ni vidu kian dosieron ni finfine ricevis kiel la unuan savŝnuron.

file firstLayer

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

tiuj. reloj estas gudro-arkivoj, malpakante ilin en la taŭga ordo ni ricevos la enhavon de la bildo.

Ni skribu malgrandan bash-skripton, por ke ĉio ĉi povu esti aŭtomatigita

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

Nun ni povas ruli ĝin kun la dezirataj parametroj kaj akiri la enhavon de la bezonata bildo

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

Parto 2 - docker-puŝo

Ĉi tio estos iom pli komplika.

Ni rekomencu per dokumentado. Do ni devas elŝuti ĉiun gvidanton, kolekti la respondan manifeston kaj elŝuti ĝin ankaŭ. Ŝajnas simple.

Post studi la dokumentadon, ni povas dividi la elŝutan procezon en plurajn paŝojn:

  • Proceza inicialigo - "POST /v2/{repoName}/blobs/uploads/"
  • Alŝuto de savŝnuro (ni uzos monolitan alŝuton, t.e. ni sendos ĉiun savŝnuron en ĝia tuteco) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Enhavo-longo: {grandeco de tavolo}
    Enhavo-Tipo: aplikaĵo/okteto-rivereto
    Tavolo Binaraj Datumoj".
  • Ŝargante la manifeston - "PUT /v2/{repoName}/manifests/{reference}".

Sed la dokumentado maltrafas unu paŝon, sen kiu nenio funkcios. Por monolita ŝarĝo, same kiel por parta (peceta), antaŭ ol ŝarĝi la relon, vi devas plenumi PATCH-peton:

"FILIKO /v2/{repoName}/blobs/uploads/{uuid}
Enhavo-longo: {grandeco de peco}
Enhavo-Tipo: aplikaĵo/okteto-rivereto
{Layer Chunk Binary Data}".

Alie, vi ne povos preterpasi la unuan punkton, ĉar... Anstataŭ la atendata respondkodo 202, vi ricevos 4xx.

Nun la algoritmo aspektas jene:

  • Inicialigo
  • Flikaĵrelo
  • Ŝargante la manbarilon
  • Ŝargante la manifeston
    La punktoj 2 kaj 3, respektive, estos ripetitaj tiom da fojoj kiom la nombro da linioj bezonas esti ŝargita.

Unue, ni bezonas ajnan bildon. Mi uzos archlinux:latest

docker pull archlinux

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Nun ni konservu ĝin loke por plia analizo

docker save c24fe13d37b9 -o savedArch

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Malpaku la rezultan arkivon en la nunan dosierujon

tar xvf savedArch

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Kiel vi povas vidi, ĉiu savŝnuro estas en aparta dosierujo. Nun ni rigardu la strukturon de la manifesto, kiun ni ricevis

cat manifest.json | json_pp

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Ne multe. Ni vidu kian manifeston necesas por ŝarĝi, laŭ dokumentado.

Efektivigo de docker pull kaj docker push-komandoj sen docker-kliento uzante HTTP-petojn

Evidente, la ekzistanta manifesto ne konvenas al ni, do ni faros nian propran per blackjack kaj korteganoj, savlinioj kaj agordoj.

Ni ĉiam havos almenaŭ unu agordosieron kaj tabelon da vivlinioj. Skemo-versio 2 (aktuala en la momento de skribado), mediaType restos senŝanĝa:

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

Post kreado de la baza manifesto, vi devas plenigi ĝin per validaj datumoj. Por fari tion, ni uzas la ŝablonon json de la fervojobjekto:

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

Ni aldonos ĝin al la manifesto por ĉiu relo.

Poste, ni devas eltrovi la grandecon de la agorda dosiero kaj anstataŭigi la stumpojn en la manifesto per realaj datumoj

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

Nun vi povas komenci la elŝutan procezon kaj konservi al vi uuid, kiu devus akompani ĉiujn postajn petojn.

La kompleta skripto aspektas kiel ĉi tio:

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

ni povas uzi pretan skripton:

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

UPS:
Kion ni ricevis kiel rezulto?
Unue, realaj datumoj por analizo, ĉar la testoj estas rulitaj en blazemeter kaj la datumoj pri docker-klientpetoj ne estas tre informaj, male al puraj HTTP-petoj.

Due, la transiro permesis al ni pliigi la nombron da virtualaj uzantoj por docker-alŝuto je ĉirkaŭ 150% kaj akiri mezan respondtempon 20-25% pli rapide. Por docker-elŝuto, ni sukcesis pliigi la nombron da uzantoj je 500%, dum meza responda tempo malpliiĝis je ĉirkaŭ 60%.

Dankon pro via atento.

fonto: www.habr.com

Aldoni komenton