Реализация Команд Docker Pull И Docker Push Без Клиента Docker С Использованием Http-Запросов

У нас были 2 пакетика травы, 75 таблеток мескалина.

среда unix, репозиторий докеров и задача реализации команд docker pull и docker push без клиента docker.

Реализация команд docker pull и docker push без клиента docker с использованием HTTP-запросов

УПД: Вопрос: Для чего все это? Отвечать: Нагрузочное тестирование продукта (НЕ с использованием bash, скрипты предоставлены в образовательных целях).

Было решено не использовать докер-клиент для уменьшения дополнительных слоев (в разумных пределах) и соответственно эмуляции более высокой нагрузки.

В результате были устранены все системные задержки клиента Docker. Мы получили относительно чистую загрузку прямо на изделии.

В статье использованы версии инструментов GNU. Для начала давайте разберемся, что делают эти команды.

Так для чего же используется docker pull? В соответствии с документация :

«Извлечь образ или репозиторий из реестра».

Там же мы находим ссылку на понимать образы, контейнеры и драйверы хранилища .



Реализация команд docker pull и docker push без клиента docker с использованием HTTP-запросов

Отсюда мы можем понять, что докер-образ — это набор определенных слоев, содержащих информацию о последних изменениях образа, что, очевидно, нам и нужно.

Далее мы рассмотрим 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"



Реализация команд 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-запросов

те.

рельсы — это 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 — докер-пуш

Это будет немного сложнее.

Давайте начнем снова с документация .

Значит нам нужно скачать каждого лидера, собрать соответствующий манифест и тоже его скачать.

Кажется, это просто.

Изучив документацию, можно разделить процесс загрузки на несколько этапов:

  • Инициализация процесса — «POST /v2/{repoName}/blobs/uploads/»
  • Загрузка линии жизни (мы будем использовать монолитную загрузку, т.е.

    отправляем каждую линию жизни целиком) — "PUT /v2/{repoName}/blobs/uploads/{uuid}Эdigest={digest} Content-Length: {размер слоя} Тип контента: приложение/октет-поток Слой двоичных данных».

  • Загрузка манифеста — «PUT /v2/{repoName}/manifests/{reference}».

Но в документации упущен один шаг, без которого ничего не будет работать.

При монолитной загрузке, как и при частичной (кусковой), перед загрузкой рельса необходимо выполнить ПАТЧ-запрос: "ИСПРАВЛЕНИЕ /v2/{repoName}/blobs/uploads/{uuid} Content-Length: {размер фрагмента} Тип контента: приложение/октет-поток {Двоичные данные фрагмента слоя}".

В противном случае вы не сможете продвинуться дальше первого пункта, потому что.

Вместо ожидаемого кода ответа 202 вы получите 4хх.

Теперь алгоритм выглядит так:

  • Инициализация
  • Патч-рейка
  • Загрузка поручня
  • Загрузка манифеста Пункты 2 и 3 соответственно будут повторяться столько раз, сколько строк необходимо загрузить.

Для начала нам понадобится любое изображение.

Я буду использовать ArchLinux: последний

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-запросов

Как видите, каждый спасательный круг находится в отдельной папке.

Теперь посмотрим на структуру полученного нами манифеста

cat manifest.json | json_pp



Реализация команд docker pull и docker push без клиента docker с использованием HTTP-запросов

Немного.

Посмотрим, какой манифест необходим для загрузки, согласно документация .



Реализация команд docker pull и docker push без клиента docker с использованием HTTP-запросов

Очевидно, существующий манифест нас не устраивает, поэтому сделаем свой.

блэкджек и куртизанки линии жизни и конфигурации.

У нас всегда будет хотя бы один файл конфигурации и массив линий жизни.

Схема версии 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 "\n\t]\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 %.

Спасибо за внимание.

Теги: #unix #docker #DevOps #bash

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.