HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Бізде 2 қап шөп, 75 мескалиндік планшеттер unix ортасы, докер репозиторийі және докер-клиентсіз докерді тарту және доккер push пәрмендерін енгізу міндеті болды.

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

UPD:
Сұрақ: Мұның бәрі не үшін?
Жауап: Өнімді жүктеп сынау (bash қолданбайды, сценарийлер білім беру мақсатында берілген). Қосымша қабаттарды азайту (ақылға қонымды шектерде) және, тиісінше, жоғары жүктемені эмуляциялау үшін докер клиентін қолданбау туралы шешім қабылданды. Нәтижесінде Docker клиентінің барлық жүйелік кешігулері жойылды. Біз салыстырмалы түрде таза жүктемені тікелей өнімге алдық.
Мақалада құралдардың GNU нұсқалары пайдаланылды.

Алдымен бұл командалардың не істейтінін анықтап алайық.

Сонымен docker pull не үшін қолданылады? Сәйкес құжаттама:

«Тіркеуден суретті немесе репозиторийді тартып алыңыз».

Онда біз сілтеме табамыз кескіндерді, контейнерлерді және сақтау драйверлерін түсіну.

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Осы жерден біз докер кескіні кескіндегі соңғы өзгерістер туралы ақпаратты қамтитын белгілі бір қабаттар жиынтығы екенін түсінуге болады, бұл бізге қажет нәрсе. Әрі қарай қараймыз тізілім API.

Онда мыналар айтылады:

""Кескін" 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"

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Жауап ретінде біз 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

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Ең бірінші құтқарушы ретінде қандай файлды алғанымызды көрейік.

file firstLayer

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

анау. рельстер - шайырлы мұрағаттар, оларды тиісті ретпен ашсақ, суреттің мазмұнын аламыз.

Мұның бәрін автоматтандыруға болатындай шағын 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}
    Мазмұн-ұзындығы: {қабат өлшемі}
    Content-Type: қолданба/октет-ағын
    Қабаттың екілік деректері».
  • Манифест жүктелуде - "PUT /v2/{repoName}/manifests/{reference}".

Бірақ құжаттама бір қадамды өткізіп жібереді, онсыз ештеңе жұмыс істемейді. Монолитті жүктеу үшін, сондай-ақ ішінара (бөлінген) рельсті жүктемес бұрын, PATCH сұрауын орындау керек:

"PATCH /v2/{repoName}/blobs/uploads/{uuid}
Мазмұн-ұзындығы: {бөлік өлшемі}
Content-Type: қолданба/октет-ағын
{Layer Chunk Binary Data}".

Әйтпесе, бірінші нүктеден аса алмайсыз, өйткені... Күтілетін 202 жауап кодының орнына сіз 4xx аласыз.

Енді алгоритм келесідей көрінеді:

  • Инициализация
  • Патч рельс
  • Тұтқаны жүктеу
  • Манифест жүктелуде
    Сәйкесінше 2 және 3 тармақтар қанша жолды жүктеу қажет болса, сонша рет қайталанады.

Біріншіден, бізге кез келген сурет керек. Мен archlinux: соңғы нұсқасын қолданамын

docker pull archlinux

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Енді оны әрі қарай талдау үшін жергілікті түрде сақтайық

docker save c24fe13d37b9 -o savedArch

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Алынған мұрағатты ағымдағы каталогқа ашыңыз

tar xvf savedArch

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Көріп отырғаныңыздай, әрбір өмір сызығы жеке қалтада. Енді біз алған манифесттің құрылымын қарастырайық

cat manifest.json | json_pp

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Көп емес. Сәйкес жүктеу үшін қандай манифест қажет екенін көрейік құжаттама.

HTTP сұрауларын пайдаланып докер клиентінсіз доккерді тарту және доккер push пәрмендерін енгізу

Қолданыстағы манифест бізге сәйкес келмейтіні анық, сондықтан біз блэкджек пен куртизан, өмірлік сызықтар мен конфигурациялар арқылы өзімізді жасаймыз.

Бізде әрқашан кем дегенде бір конфигурация файлы және өмірлік желілер массиві болады. Схеманың 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 үлгісін қолданамыз:

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

пікір қалдыру