كان لدينا حقيبتين من العشب، و2 قرص ميسكالين من بيئة يونكس، ومستودع عامل إرساء ومهمة تنفيذ أوامر سحب عامل الإرساء وأوامر دفع عامل الإرساء بدون عميل عامل إرساء.
UPD:
السؤال: لماذا كل هذا؟
الإجابة: اختبار تحميل المنتج (لا يتم استخدام bash، فالنصوص البرمجية متوفرة للأغراض التعليمية). تقرر عدم استخدام عميل عامل الإرساء لتقليل الطبقات الإضافية (ضمن حدود معقولة)، وبالتالي محاكاة حمل أعلى. ونتيجة لذلك، تمت إزالة جميع تأخيرات النظام الخاصة بعميل Docker. لقد تلقينا حمولة نظيفة نسبيًا مباشرة على المنتج.
استخدمت المقالة إصدارات GNU من الأدوات.
أولاً، دعونا نتعرف على ما تفعله هذه الأوامر.
إذن ما هو استخدام عامل الإرساء؟ وفق
"اسحب صورة أو مستودعًا من السجل".
هناك نجد أيضًا رابطًا لـ
من هنا يمكننا أن نفهم أن صورة عامل الإرساء هي مجموعة من طبقات معينة تحتوي على معلومات حول آخر التغييرات في الصورة، وهو ما نحتاجه بوضوح. التالي ننظر
تقول ما يلي:
""الصورة" عبارة عن مزيج من بيان 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"
ردًا على ذلك، نتلقى 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
دعونا نرى نوع الملف الذي تلقيناه أخيرًا باعتباره شريان الحياة الأول.
file firstLayer
أولئك. القضبان هي أرشيفات القطران، عند تفريغها بالترتيب المناسب سنحصل على محتويات الصورة.
دعونا نكتب نصًا صغيرًا لـ 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 save c24fe13d37b9 -o savedArch
قم بفك ضغط الأرشيف الناتج في الدليل الحالي
tar xvf savedArch
كما ترون، كل شريان الحياة موجود في مجلد منفصل. الآن دعونا نلقي نظرة على هيكل البيان الذي تلقيناه
cat manifest.json | json_pp
ليس كثيراً. دعونا نرى ما هو البيان المطلوب للتحميل، وفقًا لـ
من الواضح أن البيان الحالي لا يناسبنا، لذلك سنصنع بيانًا خاصًا بنا باستخدام لعبة البلاك جاك والمحظيات وشريان الحياة والتكوينات.
سيكون لدينا دائمًا ملف تكوين واحد على الأقل ومجموعة من شرايين الحياة. الإصدار 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