ما 2 کیسه چمن، 75 قرص مسکالین محیط یونیکس، یک مخزن docker و وظیفه اجرای دستورات docker pull و docker push بدون کلاینت داکر داشتیم.
UPD:
سوال: این همه برای چیست؟
پاسخ: تست بارگذاری محصول (بدون استفاده از bash، اسکریپت ها برای اهداف آموزشی ارائه شده اند). تصمیم گرفته شد که برای کاهش لایه های اضافی (در محدوده معقول) و بر این اساس، بارگذاری بالاتر از مشتری docker استفاده نشود. در نتیجه، تمام تاخیرهای سیستم مشتری Docker حذف شد. ما بار نسبتاً تمیزی را مستقیماً روی محصول دریافت کردیم.
مقاله از نسخههای گنو ابزار استفاده میکرد.
ابتدا بیایید بفهمیم که این دستورات چه کاری انجام می دهند.
بنابراین Docker pull برای چه چیزی استفاده می شود؟ مطابق با
"یک تصویر یا یک مخزن را از یک رجیستری بردارید".
در آنجا ما همچنین پیوندی به آن پیدا می کنیم
از اینجا می توانیم بفهمیم که یک تصویر داکر مجموعه ای از لایه های خاصی است که حاوی اطلاعاتی درباره آخرین تغییرات در تصویر است، که بدیهی است که ما به آن نیاز داریم. در ادامه نگاه می کنیم
در ادامه می گوید:
"تصویر" ترکیبی از یک مانیفست JSON و فایلهای لایه جداگانه است. فرآیند کشیدن یک > تصویر حول بازیابی این دو جزء متمرکز میشود."
بنابراین اولین قدم طبق مستندات این استکشیدن مانیفست تصویر".
البته، ما آن را شلیک نمی کنیم، اما به داده های آن نیاز داریم. در زیر یک نمونه درخواست است: GET /v2/{name}/manifests/{reference}
"نام و پارامتر مرجع تصویر را مشخص می کند و لازم است. مرجع ممکن است شامل یک برچسب یا خلاصه باشد."
مخزن docker ما به صورت محلی مستقر شده است، بیایید سعی کنیم درخواست را اجرا کنیم:
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}"
"دسترسی به یک لایه با نام مخزن بسته می شود، اما به طور منحصر به فرد در رجیستری توسط خلاصه شناسایی می شود."
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}".
اما مستندات یک مرحله را از دست می دهد، بدون آن هیچ چیز کار نخواهد کرد. برای بارگذاری یکپارچه، و همچنین برای بارگیری جزئی (قطع شده)، قبل از بارگیری ریل، باید یک درخواست PATCH را انجام دهید:
"PATCH /v2/{repoName}/blobs/uploads/{uuid}
طول محتوا: {اندازه تکه}
نوع محتوا: برنامه / جریان هشتگانه
{Layer Chunk Binary Data}".
در غیر این صورت نمی توانید از نقطه اول فراتر بروید، زیرا ... به جای کد پاسخ مورد انتظار 202، 4xx دریافت خواهید کرد.
اکنون الگوریتم به صورت زیر است:
- مقداردهی اولیه
- پچ ریل
- در حال بارگیری نرده
- در حال بارگیری مانیفست
نقاط 2 و 3 به ترتیب به تعداد خطوطی که باید بارگذاری شوند، تکرار خواهند شد.
ابتدا به هر تصویری نیاز داریم. من از archlinux:latest استفاده خواهم کرد
docker pull archlinux
اکنون اجازه دهید آن را به صورت محلی برای تجزیه و تحلیل بیشتر ذخیره کنیم
docker save c24fe13d37b9 -o savedArch
آرشیو به دست آمده را در دایرکتوری فعلی باز کنید
tar xvf savedArch
همانطور که می بینید، هر Lifeline در یک پوشه جداگانه قرار دارد. حال بیایید به ساختار مانیفست دریافتی خود نگاه کنیم
cat manifest.json | json_pp
زیاد نیست. بیایید ببینیم بر اساس چه چیزی برای بارگیری نیاز است
بدیهی است که مانیفست موجود برای ما مناسب نیست، بنابراین ما مانیفست خودمان را با بلاکجک و کورتیزان، لایف لاینها و تنظیمات میسازیم.
ما همیشه حداقل یک فایل پیکربندی و یک آرایه از خطوط حیاتی خواهیم داشت. طرح نسخه 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 شی 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 اجرا میشوند و دادههای مربوط به درخواستهای مشتری docker، برخلاف درخواستهای HTTP خالص، چندان آموزنده نیستند.
ثانیاً، این انتقال به ما اجازه داد تا تعداد کاربران مجازی برای بارگذاری داکر را حدود 150 درصد افزایش دهیم و میانگین زمان پاسخگویی را 20 تا 25 درصد سریعتر دریافت کنیم. برای دانلود داکر، ما موفق شدیم تعداد کاربران را 500% افزایش دهیم در حالی که میانگین زمان پاسخگویی حدود 60% کاهش یافت.
با تشکر از توجه شما.
منبع: www.habr.com