У нас были 2 пакетика травы, 75 таблеток мескалина.
среда unix, репозиторий докеров и задача реализации команд docker pull и docker push без клиента docker.
УПД: Вопрос: Для чего все это? Отвечать: Нагрузочное тестирование продукта (НЕ с использованием bash, скрипты предоставлены в образовательных целях).
Было решено не использовать докер-клиент для уменьшения дополнительных слоев (в разумных пределах) и соответственно эмуляции более высокой нагрузки.
В результате были устранены все системные задержки клиента Docker. Мы получили относительно чистую загрузку прямо на изделии.
В статье использованы версии инструментов GNU. Для начала давайте разберемся, что делают эти команды.
Так для чего же используется docker pull? В соответствии с документация :
«Извлечь образ или репозиторий из реестра».Там же мы находим ссылку на понимать образы, контейнеры и драйверы хранилища .
Отсюда мы можем понять, что докер-образ — это набор определенных слоев, содержащих информацию о последних изменениях образа, что, очевидно, нам и нужно.
Далее мы рассмотрим 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"
В ответ мы получаем 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
те.
рельсы — это 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 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
После создания базового манифеста вам необходимо заполнить его действительными данными.
Для этого мы используем 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
-
Как Восстановить Удаленные Файлы Бесплатно
19 Oct, 24 -
Парагвай
19 Oct, 24 -
Как Найти Настоящую И Надежную Seo-Компанию
19 Oct, 24