Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Մենք ունեինք 2 պարկ խոտ, 75 մեսկալինային պլանշետ unix միջավայր, դոկերի պահեստ և առաջադրանք՝ իրականացնելու docker pull և docker push հրամանները առանց docker հաճախորդի:

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

UPS:
Հարց. Ինչի՞ համար է այս ամենը:
Պատասխան՝ արտադրանքի բեռնվածության փորձարկում (ՉԻ օգտագործելով bash, սկրիպտները տրամադրվում են կրթական նպատակներով): Որոշվեց չօգտագործել docker հաճախորդը լրացուցիչ շերտերը նվազեցնելու համար (ողջամիտ սահմաններում) և, համապատասխանաբար, ավելի բարձր բեռը ընդօրինակելու համար: Արդյունքում, Docker հաճախորդի համակարգի բոլոր ուշացումները հեռացվեցին: Մենք ստացանք համեմատաբար մաքուր բեռ անմիջապես արտադրանքի վրա:
Հոդվածում օգտագործվել են գործիքների GNU տարբերակները։

Նախ, եկեք պարզենք, թե ինչ են անում այս հրամանները:

Այսպիսով, ինչի՞ համար է օգտագործվում docker pull-ը: Համաձայն փաստաթղթավորում:

«Հանեք պատկեր կամ պահեստ ռեեստրից»:

Այնտեղ մենք նաև հղում ենք գտնում հասկանալ պատկերները, բեռնարկղերը և պահեստավորման դրայվերները.

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Այստեղից մենք կարող ենք հասկանալ, որ դոկերի պատկերը որոշակի շերտերի մի շարք է, որոնք պարունակում են տեղեկատվություն պատկերի վերջին փոփոխությունների մասին, ինչը ակնհայտորեն այն է, ինչ մեզ անհրաժեշտ է: Հաջորդը մենք նայում ենք ռեեստրի API.

Այն ասում է հետևյալը.

«Պատկերը» JSON մանիֆեստի և առանձին շերտի ֆայլերի համակցություն է: Պատկերը քաշելու գործընթացը կենտրոնանում է այս երկու բաղադրիչների առբերման շուրջ:

Այսպիսով, առաջին քայլը, ըստ փաստաթղթերի, «Պատկերի մանիֆեստի քաշում".

Իհարկե, մենք չենք նկարահանի այն, բայց մեզ անհրաժեշտ են դրա տվյալները: Հետևյալը խնդրանքի օրինակ է. GET /v2/{name}/manifests/{reference}

«Անունը և հղման պարամետրը նույնականացնում են պատկերը և պարտադիր են: Հղումը կարող է ներառել պիտակ կամ ամփոփում»:

Մեր դոկերի պահեստը տեղակայված է տեղում, եկեք փորձենք կատարել հարցումը.

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 և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Ի պատասխան՝ մենք ստանում ենք json, որից մեզ ներկայումս հետաքրքրում են միայն լայֆլայները, ավելի ճիշտ՝ դրանց հեշերը։ Ստանալով դրանք՝ մենք կարող ենք անցնել յուրաքանչյուրը և կատարել հետևյալ հարցումը՝ «GET /v2/{name}/blobs/{digest}»:

«Շերտի մուտքը կփակվի պահեստի անունով, բայց գրանցամատյանում եզակիորեն ճանաչվում է ըստ ամփոփման»:

digest այս դեպքում այն ​​հեշն է, որը մենք ստացել ենք:

Արի փորձենք

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 և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Տեսնենք, թե վերջապես ինչպիսի թղթապանակ ստացանք որպես առաջին փրկօղակ։

file firstLayer

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

դրանք. ռելսերը խեժի արխիվներ են, դրանք բացելով համապատասխան հերթականությամբ՝ կստանանք պատկերի բովանդակությունը:

Եկեք մի փոքրիկ bash սցենար գրենք, որպեսզի այս ամենը ավտոմատացված լինի

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

Այժմ մենք կարող ենք այն գործարկել ցանկալի պարամետրերով և ստանալ անհրաժեշտ պատկերի բովանդակությունը

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

Մաս 2 - դոկեր հրում

Սա մի փոքր ավելի բարդ կլինի:

Սկսենք նորից փաստաթղթավորում. Այսպիսով, մենք պետք է ներբեռնենք յուրաքանչյուր առաջնորդ, հավաքենք համապատասխան մանիֆեստը և այն նույնպես ներբեռնենք: Պարզ է թվում:

Փաստաթղթերը ուսումնասիրելուց հետո մենք կարող ենք ներբեռնման գործընթացը բաժանել մի քանի փուլերի.

  • Գործընթացի սկզբնավորում – «POST /v2/{repoName}/blobs/uploads/»
  • Փրկարար գիծ վերբեռնում (մենք կօգտագործենք միաձույլ վերբեռնում, այսինքն՝ ուղարկում ենք յուրաքանչյուր փրկող օղակ ամբողջությամբ) - «PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Բովանդակություն-երկարություն՝ {շերտի չափը}
    Բովանդակություն-տեսակ՝ հավելված/օկտետ հոսք
    Շերտի երկուական տվյալներ»:
  • Մանիֆեստը բեռնվում է՝ «PUT /v2/{repoName}/manifests/{reference}»:

Բայց փաստաթղթավորումը բաց է թողնում մեկ քայլ, առանց որի ոչինչ չի ստացվի։ Միաձույլ բեռնման, ինչպես նաև մասնակի (կտրված) համար երկաթուղին բեռնելուց առաջ դուք պետք է կատարեք PATCH հարցում.

«PATCH /v2/{repoName}/blobs/uploads/{uuid}
Բովանդակություն-երկարություն՝ {չափի կտոր}
Բովանդակություն-տեսակ՝ հավելված/օկտետ հոսք
{Layer Chunk Binary Data}»:

Հակառակ դեպքում առաջին կետից այն կողմ չեք կարողանա անցնել, քանի որ... Ակնկալվող պատասխանի 202 կոդի փոխարեն դուք կստանաք 4xx:

Այժմ ալգորիթմն ունի հետևյալ տեսքը.

  • Մեկնարկումը
  • Կարկատել երկաթուղային
  • Բեռնում է բազրիքը
  • Մանիֆեստը բեռնվում է
    2-րդ և 3-րդ կետերը, համապատասխանաբար, կկրկնվեն այնքան անգամ, որքան անհրաժեշտ է բեռնել տողերի քանակը:

Նախ, մեզ անհրաժեշտ է ցանկացած պատկեր: Ես կօգտագործեմ archlinux:latest

docker pull archlinux

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Այժմ եկեք պահպանենք այն տեղական հետագա վերլուծության համար

docker save c24fe13d37b9 -o savedArch

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Ստացված արխիվը հանեք ընթացիկ գրացուցակի մեջ

tar xvf savedArch

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Ինչպես տեսնում եք, յուրաքանչյուր փրկօղակ գտնվում է առանձին թղթապանակում: Այժմ նայենք մեր ստացած մանիֆեստի կառուցվածքին

cat manifest.json | json_pp

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Ոչ շատ. Տեսնենք, թե ինչ մանիֆեստ է անհրաժեշտ բեռնելու համար՝ ըստ փաստաթղթավորում.

Docker pull և docker push հրամանների իրականացում առանց docker հաճախորդի՝ օգտագործելով HTTP հարցումները

Ակնհայտ է, որ գոյություն ունեցող մանիֆեստը մեզ չի համապատասխանում, ուստի մենք կստեղծենք մեր սեփականը բլեքջեքի և կուրտիզանների, փրկագնացների և կոնֆիգուրների միջոցով:

Մենք միշտ կունենանք առնվազն մեկ կազմաձևման ֆայլ և կյանքի գծերի զանգված: Սխեմայի տարբերակ 2 (ներկայիս գրելու պահին), mediaType-ը կմնա անփոփոխ.

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

Հիմնական մանիֆեստը ստեղծելուց հետո անհրաժեշտ է այն լրացնել վավեր տվյալներով: Դա անելու համար մենք օգտագործում ենք երկաթուղային օբյեկտի json ձևանմուշը.

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

Մենք այն կավելացնենք մանիֆեստում յուրաքանչյուր ռեյլի համար:

Հաջորդը, մենք պետք է պարզենք կազմաձևման ֆայլի չափը և մանիֆեստի կոճղերը փոխարինենք իրական տվյալներով

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

Այժմ դուք կարող եք սկսել ներբեռնման գործընթացը և ձեզ պահել uuid, որը պետք է ուղեկցի բոլոր հետագա հարցումներին:

Ամբողջական սցենարն այսպիսի տեսք ունի.

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

մենք կարող ենք օգտագործել պատրաստի սցենար.

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

UPS:
Ի՞նչ ստացանք արդյունքում։
Նախ, իրական տվյալներ վերլուծության համար, քանի որ թեստերն իրականացվում են blazemeter-ով, և docker հաճախորդի հարցումների տվյալները այնքան էլ տեղեկատվական չեն, ի տարբերություն մաքուր HTTP հարցումների:

Երկրորդ, անցումը թույլ տվեց մեզ մոտ 150%-ով ավելացնել դոկերի վերբեռնման համար վիրտուալ օգտատերերի թիվը և 20-25%-ով ավելի արագ ստանալ արձագանքման միջին ժամանակը: Docker-ի ներբեռնման համար մեզ հաջողվեց ավելացնել օգտատերերի թիվը 500%-ով, մինչդեռ միջին արձագանքման ժամանակը նվազել է մոտ 60%-ով։

Շնորհակալություն ուշադրության համար:

Source: www.habr.com

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