我們有 2 袋草、75 片麥斯卡林片劑、unix 環境、一個 docker 儲存庫,以及在沒有 docker 用戶端的情況下實作 docker pull 和 docker push 指令的任務。
UPD:
Q:這一切是為了什麼?
答案:產品的負載測試(不使用 bash,提供的腳本用於教育目的)。 決定不使用 docker 用戶端來減少額外的層(在合理的限制內),並相應地模擬更高的負載。 這樣一來,Docker 客戶端的所有系統延遲都被消除了。 我們直接在產品上收到了相對乾淨的負載。
本文使用了 GNU 版本的工具。
首先,讓我們弄清楚這些命令的作用。
那麼 docker pull 是用來做什麼的呢? 根據
「從登錄中提取映像或儲存庫」。
在那裡我們還找到了一個鏈接
從這裡我們可以理解,docker鏡像是一組某些層,其中包含鏡像最新變化的訊息,這顯然是我們所需要的。 接下來我們來看看
它說如下:
“‘鏡像’是 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}”
“對層的存取將通過存儲庫的名稱進行控制,但在註冊表中通過摘要進行唯一標識。”
在這種情況下,摘要是我們收到的雜湊值。
咱們試試吧
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
那些。 Rails 是 tar 檔案,按照適當的順序解壓縮它們,我們將獲得圖像的內容。
讓我們編寫一個小型的 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 部分 - docker 推送
這會稍微複雜一些。
讓我們重新開始
研究完文件後,我們可以將下載過程分為幾個步驟:
- 進程初始化 - “POST /v2/{repoName}/blob/uploads/”
- 上傳生命線(我們將使用整體上傳,即我們完整地發送每個生命線)-“PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
內容長度:{層的大小}
內容類型:應用程式/八位元組流
層二進位資料」。 - 載入清單 - “PUT /v2/{repoName}/manifests/{reference}”。
但該文檔遺漏了一步,沒有這一步驟,一切都將不起作用。 對於整體加載以及部分(分塊)加載,在加載導軌之前,您必須執行 PATCH 請求:
「補丁/v2/{repoName}/blob/uploads/{uuid}
內容長度:{區塊的大小}
內容類型:應用程式/八位元組流
{層塊二進位資料}」。
否則,你將無法超越第一點,因為… 您將收到 202xx,而不是預期的回應代碼 4。
現在演算法看起來像:
- 初始化
- 補軌
- 裝載扶手
- 載入清單
第 2 點和第 3 點將分別根據需要載入的行數重複多次。
首先,我們需要任何圖像。 我將使用 archlinux:latest
docker pull archlinux
現在我們將其保存在本地以供進一步分析
docker save c24fe13d37b9 -o savedArch
將產生的存檔解壓縮到目前目錄
tar xvf savedArch
如您所見,每條生命線都位於單獨的資料夾中。 現在讓我們來看看我們收到的清單的結構
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
建立基本清單後,您需要填入有效資料。 為此,我們使用 Rail 物件的 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,該 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 請求不同,docker 客戶端請求的數據資訊量不大。
其次,這項轉變使我們能夠將 docker upload 的虛擬用戶數量增加約 150%,並將平均回應時間加快 20-25%。 對於 docker download,我們成功地將使用者數量增加了 500%,而平均回應時間減少了約 60%。
感謝您的關注。
來源: www.habr.com