Вопрос или проблема
Я пытаюсь установить связь между двумя API .BET внутри контейнера Docker. Я загружаю оба API и базу данных через docker-compose, могу получить доступ к обоим API по отдельности, но когда я пытаюсь отправить данные из одного API в другой через маршрут от “API A” к “API B”, я получаю ошибку ниже.
2024-11-10 09:19:45 info: Microsoft.EntityFrameworkCore.Migrations[20405]
2024-11-10 09:19:45 Миграции не были применены. База данных уже обновлена.
2024-11-10 09:19:45 info: Microsoft.Hosting.Lifetime[14]
2024-11-10 09:19:45 Теперь слушает на: http://[::]:4000
2024-11-10 09:19:45 info: Microsoft.Hosting.Lifetime[14]
2024-11-10 09:19:45 Теперь слушает на: https://[::]:4001
2024-11-10 09:19:45 info: Microsoft.Hosting.Lifetime[0]
2024-11-10 09:19:45 Приложение запущено. Нажмите Ctrl+C для завершения работы.
2024-11-10 09:19:45 info: Microsoft.Hosting.Lifetime[0]
2024-11-10 09:19:45 Среда размещения: Разработка
2024-11-10 09:19:45 info: Microsoft.Hosting.Lifetime[0]
2024-11-10 09:19:45 Путь корневого контента: /app
2024-11-10 09:20:06 info: System.Net.Http.HttpClient.IItemRepository.LogicalHandler[100]
2024-11-10 09:20:06 Начало обработки HTTP-запроса POST https://itemservice_api/api/Item
2024-11-10 09:20:06 info: System.Net.Http.HttpClient.IItemRepository.ClientHandler[100]
2024-11-10 09:20:06 Отправка HTTP-запроса POST https://itemservice_api/api/Item
2024-11-10 09:20:06 fail: Restaurante_Api.Middlewares.ErrorHandlingMiddleware[0]
2024-11-10 09:20:06 Внутренняя ошибка сервера ErrorMessage { Code = 01.00, Message = Внутренняя ошибка сервера. }
2024-11-10 09:20:06 System.Net.Http.HttpRequestException: Соединение отказано (itemservice_api:443)
2024-11-10 09:20:06 ---> System.Net.Sockets.SocketException (111): Соединение отказано
2024-11-10 09:20:06 at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
2024-11-10 09:20:06 at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|285_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
2024-11-10 09:20:06 --- Конец трассировки стека внутреннего исключения ---
2024-11-10 09:20:06 at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(QueueItem queueItem)
2024-11-10 09:20:06 at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2024-11-10 09:20:06 at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.<SendCoreAsync>g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
2024-11-10 09:20:06 at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<SendCoreAsync>g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
2024-11-10 09:20:06 at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
2024-11-10 09:20:06 at Restaurante_Repositories.Repositories.ItemRepository.Create(CreateItemRequest item) in /src/Restaurante_Repositories/Repositories/ItemRepository.cs:line 25
2024-11-10 09:20:06 at Restaurante_UseCases.UseCases.CreateItemUseCase.Execute(CreateItemRequest request) in /src/Restaurante_UseCases/UseCases/CreateItemUseCase.cs:line 20
2024-11-10 09:20:06 at Restaurante_Api.Controllers.ItemController.Create(CreateItemRequest request) in /src/Restaurante_Api/Controllers/ItemController.cs:line 21
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
2024-11-10 09:20:06 at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
2024-11-10 09:20:06 at Restaurante_Api.Middlewares.ErrorHandlingMiddleware.Invoke(HttpContext context, ILogger`1 logger) in /src/Restaurante_Api/Middlewares/ErrorHandlingMiddleware.cs:line 16
Я собрал docker-compose следующим образом
services:
restaurante_api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=4000
- ASPNETCORE_HTTPS_PORTS=4001
container_name: restaurante_api
hostname: restaurante_api
image: ${DOCKER_REGISTRY-}restauranteapi
build:
context: .
dockerfile: Restaurante_Api/Dockerfile
ports:
- "4000:4000"
- "4001:4001"
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
- ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
networks:
restaurante-bridge:
depends_on:
itemservice_api:
condition: service_started
sqlserver:
condition: service_healthy
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-preview-ubuntu-22.04
container_name: sqlserver
hostname: sqlserver
ports:
- "1433:1433"
environment:
- SA_PASSWORD=leoQueres123
- ACCEPT_EULA= Y
volumes:
- ${APPDATA}/sqlserverrestaurante/data:/var/opt/mssql/data
- ${APPDATA}/sqlserverrestaurante/log:/var/opt/mssql/log
networks:
- restaurante-bridge
healthcheck:
test: /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "leoQueres123" -Q "SELECT 1" -b -o /dev/null
interval: 10s
timeout: 3s
retries: 10
start_period: 10s
itemservice_api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=5000
- ASPNETCORE_HTTPS_PORTS=5001
container_name: itemservice_api
hostname: itemservice_api
image: ${DOCKER_REGISTRY-}itemserviceapi
build:
context: .
dockerfile: ItemService_Api/Dockerfile
ports:
- "5000:5000"
- "5001:5001"
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
- ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
networks:
restaurante-bridge:
depends_on:
sqlserver:
condition: service_healthy
networks:
restaurante-bridge:
driver: bridge
external: true
docker-compose:
restaurante-bridge:
driver: bridge`
Dockerfile каждого приложения
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 5000
EXPOSE 5001
# Эта фаза используется для компиляции проекта сервиса
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["ItemService_Api/ItemService_Api.csproj", "ItemService_Api/"]
COPY ["ItemService_Borders/ItemService_Borders.csproj", "ItemService_Borders/"]
COPY ["ItemService_Repositories/ItemService_Repositories.csproj", "ItemService_Repositories/"]
COPY ["ItemService_UseCases/ItemService_UseCases.csproj", "ItemService_UseCases/"]
RUN dotnet restore "./ItemService_Api/ItemService_Api.csproj"
COPY . .
WORKDIR "/src/ItemService_Api"
RUN dotnet tool install --global dotnet-ef
ENV PATH="$PATH:/root/.dotnet/tools"
RUN dotnet build "./ItemService_Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Эта фаза используется для публикации проекта сервиса, который будет скопирован в финальную фазу
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./ItemService_Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Эта фаза используется в производстве или когда выполняется в VS в обычном режиме (стандартный режим, когда не используется конфигурация отладки)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT dotnet ef database update --environment Development --project src/ItemService_Repositories
ENTRYPOINT ["dotnet", "ItemService_Api.dll"]
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 4000
EXPOSE 4001
# Эта фаза используется для компиляции проекта сервиса
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Restaurante_Api/Restaurante_Api.csproj", "Restaurante_Api/"]
COPY ["Restaurante_Borders/Restaurante_Borders.csproj", "Restaurante_Borders/"]
COPY ["Restaurante_Repositories/Restaurante_Repositories.csproj", "Restaurante_Repositories/"]
COPY ["Restaurante_UseCases/Restaurante_UseCases.csproj", "Restaurante_UseCases/"]
RUN dotnet restore "./Restaurante_Api/Restaurante_Api.csproj"
COPY . .
WORKDIR "/src/Restaurante_Api"
RUN dotnet tool install --global dotnet-ef
ENV PATH="$PATH:/root/.dotnet/tools"
RUN dotnet build "./Restaurante_Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Эта фаза используется для публикации проекта сервиса, который будет скопирован в финальную фазу
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Restaurante_Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Эта фаза используется в производстве или когда выполняется в VS в обычном режиме (стандартный режим, когда не используется конфигурация отладки)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT dotnet ef database update --environment Development --project src/Restaurante_Repositories
ENTRYPOINT ["dotnet", "Restaurante_Api.dll"]
Может кто-нибудь помочь мне понять эту проблему? Я сделал все, что мог, и не могу это решить.
Ответ или решение
Для успешного взаимодействия между двумя .NET API в контейнерах Docker, необходимо учесть несколько ключевых аспектов, касающихся настройки сети, конфигурации и обработки ошибок. Ваша проблема, судя по предоставленным логам и конфигурации, заключается в невозможности API A установить соединение с API B, что вызывает ошибку "Connection refused".
Анализ проблемы
Ошибка HttpRequestException: Connection refused (itemservice_api:443)
указывает на то, что API A не может подключиться к API B по указанному адресу и порту. Это может быть вызвано несколькими факторами:
-
Сетевые настройки: Убедитесь, что оба контейнера находятся в одной сети
restaurante-bridge
. Судя по вашемуdocker-compose.yml
, они действительно находятся в одной сети, но стоит перепроверить. -
Протоколы и порты: В ваших API вы указали, что они работают на HTTPS, однако в запросе вы пытаетесь подключиться к
itemservice_api:443
, что является стандартным портом для HTTPS. Проверьте, что API B слушает на этом порту, и корректно настроен для обработки HTTPS-запросов. -
Работа с сертификатами: Если ваше приложение использует HTTPS, убедитесь, что сертификат для HTTPS корректно настроен внутри контейнера. Можно временно отключить HTTPS в процессе отладки, чтобы проверить, воспроизводится ли проблема.
-
Задержки при запуске: Контейнеры могут не быть готовы к моменту, когда вы пытаетесь отправить запрос. Убедитесь, что API B полностью инициализирован и слушает на порту, прежде чем API A попытается установить соединение.
Рекомендации по решению
-
Проверка конфигурации сети:
Убедитесь, что вы можете пинговать контейнеры друг с другом. Это можно сделать с помощью команды:docker exec -it <container_name> ping itemservice_api
-
Проверка доступности API B:
Используйте командуcurl
из контейнера API A, чтобы проверить, доступен ли API B:curl -v https://itemservice_api:5001/api/Item
Замените порт на 5001 для HTTPS, основываясь на вашей конфигурации.
-
Настройка логирования:
Добавьте более подробное логирование в API B, чтобы понимать, принимает ли он запросы и что делает с ними. -
Изменение порта:
Если вы продолжаете сталкиваться с проблемами подключения, вы можете временно изменить API B на использование HTTP с открытым портом, чтобы исключить проблемы с сертификатом. -
Задержка на запуске:
Добавьте зависимостьdepends_on
в вашемdocker-compose.yml
, убедившись, что она настроена и на уровень готовности Service B:- itemservice_api: condition: service_healthy
Заключение
Убедитесь, что все компоненты вашей системы настроены корректно и следуйте приведенным выше рекомендациям для диагностики. Если вам потребуется дополнительная помощь, предоставьте больше информации о конфигурации ответов сервиса и среду выполнения приложения. Подходите к проблеме системно и на уровне сети, это поможет избежать большинства распространенных ошибок при работе с контейнеризацией.