تنفيذ أوامر docker pull وdocker Push بدون استخدام عميل docker لطلبات HTTP

كان لدينا حقيبتين من العشب، و2 قرص ميسكالين من بيئة يونكس، ومستودع عامل إرساء ومهمة تنفيذ أوامر سحب عامل الإرساء وأوامر دفع عامل الإرساء بدون عميل عامل إرساء.

تنفيذ أوامر docker pull وdocker Push بدون استخدام عميل docker لطلبات HTTP

UPD:
السؤال: لماذا كل هذا؟
الإجابة: اختبار تحميل المنتج (لا يتم استخدام bash، فالنصوص البرمجية متوفرة للأغراض التعليمية). تقرر عدم استخدام عميل عامل الإرساء لتقليل الطبقات الإضافية (ضمن حدود معقولة)، وبالتالي محاكاة حمل أعلى. ونتيجة لذلك، تمت إزالة جميع تأخيرات النظام الخاصة بعميل Docker. لقد تلقينا حمولة نظيفة نسبيًا مباشرة على المنتج.
استخدمت المقالة إصدارات GNU من الأدوات.

أولاً، دعونا نتعرف على ما تفعله هذه الأوامر.

إذن ما هو استخدام عامل الإرساء؟ وفق توثيق:

"اسحب صورة أو مستودعًا من السجل".

هناك نجد أيضًا رابطًا لـ فهم الصور والحاويات وبرامج تشغيل التخزين.

تنفيذ أوامر docker pull وdocker Push بدون استخدام عميل docker لطلبات HTTP

من هنا يمكننا أن نفهم أن صورة عامل الإرساء هي مجموعة من طبقات معينة تحتوي على معلومات حول آخر التغييرات في الصورة، وهو ما نحتاجه بوضوح. التالي ننظر واجهة برمجة تطبيقات التسجيل.

تقول ما يلي:

""الصورة" عبارة عن مزيج من بيان 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}"

"سيتم تحديد الوصول إلى الطبقة من خلال اسم المستودع، ولكن يتم تحديده بشكل فريد في السجل من خلال الملخص."

الملخص في هذه الحالة هو التجزئة التي تلقيناها.

محاولة

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}".

لكن التوثيق يفتقد خطوة واحدة، والتي بدونها لن ينجح أي شيء. بالنسبة للتحميل المتجانس، وكذلك للتحميل الجزئي (المقطع)، قبل تحميل السكة، يجب عليك تنفيذ طلب التصحيح:

"التصحيح /v2/{repoName}/blobs/uploads/{uuid}
طول المحتوى: {حجم القطعة}
نوع المحتوى: تطبيق / ثماني دفق
{البيانات الثنائية لقطعة الطبقة}".

وإلا فلن تتمكن من تجاوز النقطة الأولى، لأن... بدلا من رمز الاستجابة المتوقع 202، سوف تتلقى 4xx.

الآن تبدو الخوارزمية كما يلي:

  • التهيئة
  • السكك الحديدية التصحيح
  • تحميل الدرابزين
  • تحميل البيان
    سيتم تكرار النقطتين 2 و3، على التوالي، عدة مرات حسب عدد الأسطر التي يجب تحميلها.

أولا، نحن بحاجة إلى أي صورة. سأستخدم أركلينكس: الأحدث

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 من المخطط (الحالي وقت كتابة هذا التقرير)، سيتم ترك نوع الوسائط دون تغيير:

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 الخاص بكائن Rail:

{
         "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

UPD:
ماذا حصلنا نتيجة لذلك؟
أولاً، البيانات الحقيقية للتحليل، حيث يتم إجراء الاختبارات في blazemeter والبيانات المتعلقة بطلبات عميل عامل الإرساء ليست مفيدة للغاية، على عكس طلبات HTTP الخالصة.

ثانيًا، سمح لنا النقل بزيادة عدد المستخدمين الظاهريين لتحميل عامل الإرساء بحوالي 150% والحصول على متوسط ​​وقت استجابة أسرع بنسبة 20-25%. بالنسبة لتنزيل عامل الإرساء، تمكنا من زيادة عدد المستخدمين بنسبة 500%، بينما انخفض متوسط ​​وقت الاستجابة بحوالي 60%.

شكرا لكم على اهتمامكم.

المصدر: www.habr.com

إضافة تعليق