HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Bizda 2 qop o't, 75 ta meskalin planshetlari unix muhiti, docker ombori va docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish vazifasi bor edi.

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

UPS:
Savol: Bularning barchasi nima uchun?
Javob: Mahsulotni yuklash testi (bash-dan foydalanmang, skriptlar o'quv maqsadlarida taqdim etiladi). Qo'shimcha qatlamlarni (oqilona chegaralar ichida) kamaytirish va shunga mos ravishda yuqori yukni taqlid qilish uchun docker mijozidan foydalanmaslikka qaror qilindi. Natijada, Docker mijozining barcha tizim kechikishlari olib tashlandi. Biz to'g'ridan-to'g'ri mahsulotga nisbatan toza yuk oldik.
Maqolada vositalarning GNU versiyalari ishlatilgan.

Birinchidan, bu buyruqlar nima qilishini aniqlaymiz.

Xo'sh, docker pull nima uchun ishlatiladi? Ga binoan hujjatlar:

"Ro'yxatga olish kitobidan rasm yoki omborni tortib oling".

U erda biz havolani ham topamiz tasvirlar, konteynerlar va saqlash drayverlarini tushunish.

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Bu erdan tushunishimiz mumkinki, docker tasviri - bu tasvirdagi so'nggi o'zgarishlar haqidagi ma'lumotlarni o'z ichiga olgan ma'lum qatlamlar to'plami, bu bizga kerak bo'lgan narsadir. Keyinchalik ko'rib chiqamiz registr API.

Unda quyidagilar aytiladi:

"Tasvir" JSON manifest va individual qatlam fayllari birikmasidir. > Rasmni tortib olish jarayoni ushbu ikki komponentni olish atrofida markazlashadi."

Shunday qilib, hujjatlarga muvofiq birinchi qadam "Tasvir manifestini tortib olish".

Albatta, biz uni o'qqa tutmaymiz, lekin bizga undan ma'lumotlar kerak. Quyida so'rov namunasi keltirilgan: GET /v2/{name}/manifests/{reference}

"Ism va mos yozuvlar parametri rasmni aniqlaydi va talab qilinadi. Ma'lumotnoma teg yoki dayjestni o'z ichiga olishi mumkin."

Bizning docker omborimiz mahalliy sifatida joylashtirilgan, keling, so'rovni bajarishga harakat qilaylik:

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

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Bunga javoban biz json-ni olamiz, undan biz hozirda faqat hayot chiziqlari, aniqrog'i ularning xeshlari bilan qiziqamiz. Ularni qabul qilib, biz har birini ko'rib chiqishimiz va quyidagi so'rovni bajarishimiz mumkin: "GET /v2/{name}/blobs/{digest}"

"Qatlamga kirish repozitariy nomi bilan ochiladi, ammo registrda dayjest orqali noyob tarzda aniqlanadi."

bu holda digest - biz olgan xesh.

Urinish

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

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Keling, birinchi hayot chizig'i sifatida nihoyat qanday faylni olganimizni ko'rib chiqaylik.

file firstLayer

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

bular. relslar tar arxivlari bo'lib, ularni tegishli tartibda ochib, biz rasmning mazmunini olamiz.

Keling, bularning barchasini avtomatlashtirish uchun kichik bash skriptini yozaylik

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

Endi biz uni kerakli parametrlar bilan ishga tushirishimiz va kerakli tasvirning mazmunini olishimiz mumkin

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

2-qism - docker push

Bu biroz murakkabroq bo'ladi.

Keling, yana boshlaylik hujjatlar. Shunday qilib, biz har bir rahbarni yuklab olishimiz, tegishli manifestni yig'ishimiz va uni ham yuklab olishimiz kerak. Bu oddiy ko'rinadi.

Hujjatlarni o'rganib chiqqandan so'ng, yuklab olish jarayonini bir necha bosqichlarga bo'lishimiz mumkin:

  • Jarayonni ishga tushirish - "POST /v2/{repoName}/blobs/uploads/"
  • Hayot chizig'ini yuklash (biz monolit yuklashdan foydalanamiz, ya'ni har bir hayot chizig'ini to'liq yuboramiz) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    Kontent uzunligi: {qatlam hajmi}
    Kontent turi: dastur/oktet-oqim
    Ikkilik ma'lumotlar qatlami".
  • Manifest yuklanmoqda - "PUT /v2/{repoName}/manifests/{reference}".

Ammo hujjatlar bir qadamni o'tkazib yuboradi, ularsiz hech narsa ishlamaydi. Monolit yuklash uchun, shuningdek qisman (parchalangan) uchun relsni yuklashdan oldin siz PATCH so'rovini bajarishingiz kerak:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Kontent uzunligi: {bo'lak hajmi}
Kontent turi: dastur/oktet-oqim
{Layer Chunk Binary Data}".

Aks holda, siz birinchi nuqtadan nariga o'ta olmaysiz, chunki... Kutilgan javob kodi 202 o'rniga siz 4xx olasiz.

Endi algoritm quyidagicha ko'rinadi:

  • Boshlash
  • Yamoqli temir yo'l
  • Tutqich yuklanmoqda
  • Manifest yuklanmoqda
    2 va 3-bandlar, navbati bilan, qancha qatorlarni yuklash kerak bo'lsa, shuncha takrorlanadi.

Birinchidan, bizga har qanday rasm kerak. Men archlinuxdan foydalanaman: eng yangi

docker pull archlinux

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Endi keyingi tahlil qilish uchun uni mahalliy sifatida saqlaymiz

docker save c24fe13d37b9 -o savedArch

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Olingan arxivni joriy katalogga oching

tar xvf savedArch

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Ko'rib turganingizdek, har bir hayot chizig'i alohida papkada. Endi biz olgan manifestning tuzilishini ko'rib chiqamiz

cat manifest.json | json_pp

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Unchalik emas. Keling, ko'ra, qanday manifestni yuklash kerakligini ko'rib chiqaylik hujjatlar.

HTTP so'rovlaridan foydalangan holda docker mijozisiz docker pull va docker push buyruqlarini amalga oshirish

Shubhasiz, mavjud manifest bizga mos kelmaydi, shuning uchun biz blackjack va courtesans, lifelines va configs yordamida o'zimizni yaratamiz.

Bizda har doim kamida bitta konfiguratsiya fayli va hayot liniyalari qatori bo'ladi. Sxemaning 2-versiyasi (yozilish vaqtidagi), mediaType o'zgarishsiz qoladi:

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

Asosiy manifestni yaratgandan so'ng, uni haqiqiy ma'lumotlar bilan to'ldirishingiz kerak. Buning uchun biz rail ob'ektining json shablonidan foydalanamiz:

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

Biz uni har bir rels uchun manifestga qo'shamiz.

Keyinchalik, konfiguratsiya faylining hajmini bilib olishimiz va manifestdagi stublarni haqiqiy ma'lumotlar bilan almashtirishimiz kerak.

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

Endi siz yuklab olish jarayonini boshlashingiz va o'zingizga uuid-ni saqlashingiz mumkin, bu keyingi barcha so'rovlarga hamroh bo'lishi kerak.

To'liq skript quyidagicha ko'rinadi:

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

Biz tayyor skriptdan foydalanishimiz mumkin:

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

UPS:
Natijada nimaga erishdik?
Birinchidan, tahlil uchun haqiqiy ma'lumotlar, chunki testlar blazemeterda o'tkaziladi va docker mijoz so'rovlari bo'yicha ma'lumotlar sof HTTP so'rovlaridan farqli o'laroq unchalik ma'lumotli emas.

Ikkinchidan, o'tish bizga docker yuklash uchun virtual foydalanuvchilar sonini taxminan 150% ga oshirish va o'rtacha javob vaqtini 20-25% tezroq olish imkonini berdi. Docker yuklab olish uchun biz foydalanuvchilar sonini 500% ga oshirishga muvaffaq bo'ldik, o'rtacha javob vaqti esa taxminan 60% ga kamaydi.

E'tiboringiz uchun rahmat.

Manba: www.habr.com

a Izoh qo'shish