การใช้คำสั่ง docker pull และ docker push โดยไม่มีไคลเอ็นต์ docker โดยใช้คำขอ HTTP

เรามีหญ้า 2 ถุง สภาพแวดล้อมยูนิกซ์แท็บเล็ตมอมเมา 75 เม็ด พื้นที่เก็บข้อมูลนักเทียบท่า และงานในการใช้คำสั่งดึงนักเทียบท่าและคำสั่งกดนักเทียบท่าโดยไม่ต้องใช้ไคลเอ็นต์นักเทียบท่า

การใช้คำสั่ง docker pull และ docker push โดยไม่มีไคลเอ็นต์ docker โดยใช้คำขอ HTTP

ยูพีเอส:
คำถาม: ทั้งหมดนี้เพื่ออะไร?
คำตอบ: การทดสอบโหลดของผลิตภัณฑ์ (ไม่ได้ใช้ bash สคริปต์มีไว้เพื่อการศึกษา) มีการตัดสินใจว่าจะไม่ใช้ไคลเอ็นต์นักเทียบท่าเพื่อลดเลเยอร์เพิ่มเติม (ภายในขอบเขตที่เหมาะสม) และด้วยเหตุนี้จึงจำลองโหลดที่สูงกว่า ด้วยเหตุนี้ ความล่าช้าของระบบทั้งหมดของไคลเอ็นต์ Docker จึงถูกลบออก เราได้รับปริมาณงานที่ค่อนข้างสะอาดบนผลิตภัณฑ์โดยตรง
บทความนี้ใช้เครื่องมือเวอร์ชัน GNU

ก่อนอื่น เรามาดูกันว่าคำสั่งเหล่านี้ทำอะไร

ดังนั้น docker pull ใช้ทำอะไร? ตาม เอกสาร:

"ดึงรูปภาพหรือพื้นที่เก็บข้อมูลจากรีจิสตรี"

ที่นั่นเรายังพบลิงค์ไปยัง เข้าใจอิมเมจ คอนเทนเนอร์ และไดรเวอร์การจัดเก็บข้อมูล.

การใช้คำสั่ง docker pull และ docker push โดยไม่มีไคลเอ็นต์ docker โดยใช้คำขอ HTTP

จากจุดนี้ เราสามารถเข้าใจได้ว่า Docker Image คือชุดของเลเยอร์บางเลเยอร์ที่มีข้อมูลเกี่ยวกับการเปลี่ยนแปลงล่าสุดในรูปภาพ ซึ่งเห็นได้ชัดว่าเป็นสิ่งที่เราต้องการ ต่อไปเรามาดูกัน API รีจิสทรี.

มันบอกว่าต่อไปนี้:

""รูปภาพ" คือการรวมกันของไฟล์ Manifest 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

เหล่านั้น. rails เป็นไฟล์เก็บถาวร tar การแกะออกตามลำดับที่เหมาะสมเราจะได้รับเนื้อหาของรูปภาพ

มาเขียนสคริปต์ทุบตีเล็กๆ เพื่อให้ทั้งหมดนี้สามารถทำงานอัตโนมัติได้

#!/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/"
  • การอัปโหลด lifeline (เราจะใช้การอัปโหลดแบบเสาหิน กล่าวคือ เราส่ง lifeline แต่ละอันอย่างครบถ้วน) - "PUT /v2/{repoName}/blobs/uploads/{uuid}?digest={digest}
    ความยาวเนื้อหา: {ขนาดของเลเยอร์}
    ประเภทเนื้อหา: application/octet-stream
    ข้อมูลไบนารีชั้น"
  • กำลังโหลดรายการ - "PUT /v2/{repoName}/manifests/{reference}"

แต่เอกสารประกอบขาดขั้นตอนเดียวโดยที่ไม่มีอะไรทำงาน สำหรับการโหลดแบบเสาหิน เช่นเดียวกับบางส่วน (เป็นก้อน) ก่อนที่จะโหลดราง คุณต้องดำเนินการคำขอ PATCH:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
ความยาวเนื้อหา: {ขนาดของชิ้น}
ประเภทเนื้อหา: application/octet-stream
{ข้อมูลไบนารีของเลเยอร์ก้อน}"

ไม่เช่นนั้นจะไม่สามารถก้าวข้ามจุดแรกได้เพราะ... แทนที่จะเป็นรหัสตอบกลับที่คาดไว้ 202 คุณจะได้รับ 4xx

ตอนนี้อัลกอริทึมดูเหมือนว่า:

  • การเริ่มต้น
  • รางแพทช์
  • กำลังโหลดราวจับ
  • กำลังโหลดรายการ
    จุดที่ 2 และ 3 ตามลำดับ จะถูกทำซ้ำหลาย ๆ ครั้งตามจำนวนบรรทัดที่ต้องโหลด

ก่อนอื่น เราต้องการรูปภาพใดๆ ฉันจะใช้archlinux:latest

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

อย่างที่คุณเห็น เส้นชีวิตแต่ละอันอยู่ในโฟลเดอร์แยกกัน ตอนนี้เรามาดูโครงสร้างของ manifest ที่เราได้รับกัน

cat manifest.json | json_pp

การใช้คำสั่ง docker pull และ docker push โดยไม่มีไคลเอ็นต์ docker โดยใช้คำขอ HTTP

ไม่มาก. เรามาดูกันว่ารายการใดที่จำเป็นในการโหลดตาม เอกสาร.

การใช้คำสั่ง docker pull และ docker push โดยไม่มีไคลเอ็นต์ docker โดยใช้คำขอ HTTP

แน่นอนว่าแถลงการณ์ที่มีอยู่ไม่เหมาะกับเรา ดังนั้นเราจะสร้างแบล็คแจ็คและโสเภณี เส้นชีวิต และการกำหนดค่าของตัวเองขึ้นมาเอง

เราจะมีไฟล์กำหนดค่าอย่างน้อยหนึ่งไฟล์และอาร์เรย์ของเส้นชีวิตเสมอ Scheme เวอร์ชัน 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]}"
      },

เราจะเพิ่มลงในรายการสำหรับแต่ละราง

ต่อไป เราจำเป็นต้องค้นหาขนาดของไฟล์กำหนดค่าและแทนที่ stubs ในรายการด้วยข้อมูลจริง

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

ยูพีเอส:
เราได้รับผลอะไร?
ประการแรก ข้อมูลจริงสำหรับการวิเคราะห์ เนื่องจากการทดสอบดำเนินการใน blazemeter และข้อมูลในคำขอไคลเอ็นต์นักเทียบท่านั้นไม่ได้ให้ข้อมูลมากนัก ต่างจากคำขอ HTTP ล้วนๆ

ประการที่สอง การเปลี่ยนแปลงทำให้เราสามารถเพิ่มจำนวนผู้ใช้เสมือนสำหรับการอัปโหลดนักเทียบท่าได้ประมาณ 150% และรับเวลาตอบสนองโดยเฉลี่ยเร็วขึ้น 20-25% สำหรับการดาวน์โหลดนักเทียบท่า เราจัดการเพื่อเพิ่มจำนวนผู้ใช้ได้ 500% ในขณะที่เวลาตอบสนองโดยเฉลี่ยลดลงประมาณ 60%

ขอบคุณสำหรับความสนใจของคุณ

ที่มา: will.com

เพิ่มความคิดเห็น