Swagger конейнер на основе Windows в Docker
Введение | |
Структура проекта | |
Код докерфайла | |
Сборка и публикация | |
Статьи про Docker |
Введение
Допустим существует какой-то сервер с API, к которому нативно не приделали Swagger. Для простоты будем называть его
API
Он может крутиться в Docker контейнере на основе Windows. Стоит задача поднять рабочий Swagger чтобы из него демонстировать
Try it out запросы.
Можно добавить его в существующий контейнер либо поднять рядом новый.
Будем поднимать рядом новый руководствовуясь принципом
Один сервис - один конейнер
Структура проекта
Так как конейнер на базе Windows, в этом проекте будет использоваться Docker с
Windows engine
, соответственно все остальные контейнеры будут на базе
Windows
то есть использовать нативный контейнер swaggerapi/swagger-ui не получится.
Из секции Plain old HTML/CSS/JS (Standalone)
официальной инструкции
по установке Swagger мы можем узнать, что скачать актуальную версию в виде архива можно
здесь
.
В этом примере используется версия
5.11.10
скачивать и настраивать её мы будем
PowerShell
скриптом, но советую также скачать эту директорию вручную и изучить содержимое. Основная директория, в которой мы будет работать - это dist
separate_containers/ |-- docker-compose.yml `-- dockerfiles |-- Dockerfile.server |-- Dockerfile.swagger.python |-- setup.ps1 |-- src | `-- bin | `-- Server.exe `-- swagger_config `- dist |-- swagger-initializer.js `-- swagger.json
Dockerfile.server
и директория src/bin
относятся к API.
Нас мало интересует что там внутри, главное чтобы докер образ подходил нашей
Windows
Допустим
докер файл
выглядит так
# Dockerfile.server # Host: Microsoft Windows [Version 10.0.22621.3155] FROM mcr.microsoft.com/windows/nanoserver:ltsc2022 COPY src/bin/ /bin EXPOSE 9500 WORKDIR /bin ENTRYPOINT ["Server.exe"]
Dockerfile.swagger.python нужен нам для запуска http сервера в директории swagger-ui/dist
# Dockerfile.swagger.python # Host: Microsoft Windows [Version 10.0.22621.3155] FROM python:3.12.2-windowsservercore LABEL author="https://devhops.ru" COPY swagger-ui /swagger-ui WORKDIR /swagger-ui/dist ENTRYPOINT [ "python", "-m", "http.server" ]
Если по какой-то причине вам не подходит образ на основе Python можно использовать образ servercore и установить туда Node.js
# Dockerfile.swagger.node # Host: Microsoft Windows [Version 10.0.22621.3155] FROM mcr.microsoft.com/windows/servercore:ltsc2022 LABEL author="www.DevHops.ru" RUN powershell.exe -Command \ Invoke-WebRequest -OutFile nodejs.zip -UseBasicParsing "https://nodejs.org/download/release/v16.20.2/node-v16.20.2-win-x64.zip"; \ Expand-Archive -Path nodejs.zip -DestinationPath C:\; \ Rename-Item "C:\node-v16.20.2-win-x64" C:\nodejs; WORKDIR "C:\nodejs" RUN setx path "%path%;C:\nodejs" WORKDIR /bin COPY swagger-ui /swagger-ui RUN npm install -g http-server WORKDIR /swagger-ui/dist ENTRYPOINT [ "http-server", "--cors" ]
docker-compose.yml
Простой compose файл docker-compose.yml связывает контейнеры вместе.
# docker-compose.yml services: compose-swagger-win: build: context: ./dockerfiles dockerfile: Dockerfile.swagger.python ports: - "8002:8000" container_name: compose_swagger_win compose-colreg-server: build: context: ./dockerfiles dockerfile: Dockerfile.colreg ports: - "9500:9801" container_name: compose_colreg_win
РЕКЛАМА от Яндекса. Может быть недоступна в вашем регионе
Конец рекламы. Если там пусто считайте это рекламой моей телеги
Нужно подготовить файл с описание API. Раз это не сделано автоматически, значит придётся наскебать информации по фрагментам, например
по телам запросов и так далее.
Можно предварительно установить Swagger editor и там писать openapi.yaml файл а потом
сконвертировать его в JSON
Так или иначе нужно подготовить
swagger.json
{ "openapi": "3.0.3", "info": { "title": "www.TopBicycle.ru", "version": "0.1.0" }, "servers": [ { "url": "http://localhost:9500" } ], "paths": { "/endpoint": { "post": { "summary": "Update bike Info", "operationId": "update_bike_info_bikes_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Body_update_bike_info_bikes_post" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "headers": { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "*", "Access-Control-Allow-Headers": "*" }, "content": { "application/json": { "schema": {} } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } } }, "components": { "schemas": { "Body_update_bike_info_bikes_post": { "type": "array", "items": { "$ref": "#/components/schemas/bikes" }, "required": [ "bike1", "bike2" ], "title": "Body_update_bike_info_bikes_post" }, "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "type": "array", "title": "Detail" } }, "type": "object", "title": "HTTPValidationError" }, "Info": { "properties": { "id": { "type": "integer", "title": "Id" }, "name": { "type": "string", "title": "Name" }, "manuf": { "type": "string", "title": "Manufacturer" }, "type": "object", "required": [ "id", "name", "manuf" ], "title": "Info" } }, "Bikes": { "properties": { "bike1": { "$ref": "#/components/schemas/Bike" }, "bike2": { "$ref": "#/components/schemas/Bike" } }, "required": [ "bike1", "bike2" ] }, "Bike": { "properties": { "info": { "$ref": "#/components/schemas/Info" }, "type": "object", "required": [ "info" ], "title": "bike" } } } } }
Затем подоговим файл swagger-initializer.js
window.onload = function() { //<editor-fold desc="Changeable Configuration Block"> // the following lines will be replaced by docker/configurator, when it runs in a docker-container window.ui = SwaggerUIBundle({ url: "swagger.json", dom_id: '#swagger-ui', deepLinking: true, presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], plugins: [ SwaggerUIBundle.plugins.DownloadUrl ], layout: "StandaloneLayout" }); //</editor-fold> };
Если бы мы запускали нативный контейнер, то, скорее-всего пришлось бы редактировать файл translator.js из swagger-ui/docker/configurator
… url: { value: "swagger.json", schema: { type: "string", base: true } }, …
Но нам это сейчас не нужно. Эти файлы можно предварительно скачать здесь как уже было сказано в начале статьи.
Сборка и запуск
Собирать и запускать проект будет скрипт setup.ps1 который сперва скачает и распакует архив со swagger-ui затем подставит туда заранее приготовленные файлы swagger.json и swagger-initializer.js и затем выполнит команды docker compose up --build
$SWAGGER_VERSION = "5.11.10" $SWAGGER_URL = "https://github.com/swagger-api/swagger-ui/archive/refs/tags/v${SWAGGER_VERSION}.zip"; $SWAGGER_DIR = "swagger-ui" $swagger_ui_path = Join-Path ${pwd} ${SWAGGER_DIR}; $json_path = Join-Path ${pwd} "swagger_config\dist\swagger.json"; $initializer_path = Join-Path ${pwd} "swagger_config\dist\swagger-initializer.js"; # $translator_path = Join-Path ${pwd} "swagger_config\docker\configurator\translator.js"; $dist = Join-Path ${pwd} "${SWAGGER_DIR}\dist\"; # $configurator = Join-Path ${pwd} "${SWAGGER_DIR}\docker\configurator\"; If (Test-Path -path ${swagger_ui_path}) { Write-Host "${SWAGGER_DIR} dir exists" -f Green; } Else { Write-Host "${SWAGGER_DIR} dir does not exist - starting download" -f Yellow Invoke-WebRequest $SWAGGER_URL -OutFile swagger-ui.zip Expand-Archive -Path swagger-ui.zip -DestinationPath . Remove-Item swagger-ui.zip Rename-Item -Path "swagger-ui-${SWAGGER_VERSION}" -NewName ${SWAGGER_DIR} Copy-Item -Path ${json_path} -Destination ${dist} -Recurse -force Copy-Item -Path ${initializer_path} -Destination ${dist} -Recurse -force # Copy-Item -Path ${translator_path} -Destination ${configurator} -Recurse -force } docker compose build docker compose up
После завершения сборки нужно будет в браузере, например в Firefox открыть адрес
localhost:8002
И в открывшемся Swagger UI выполнять запросы Try it out к API