# #spring-boot #rest #cloud #google-cloud-run #outbound
Вопрос:
Я развернул контейнер в Google Cloud Run. Это приложение, встроенное в Spring boot и Java.
Я предоставил конечную точку(контроллер rest) контейнеру облачного запуска, который далее вызывает API REST для попадания во внешнюю конечную точку, и в этот момент происходит сбой. Я попадаю в открытую конечную точку от Почтальона.
Контейнер способен прослушивать входящие запросы и даже может подключаться к облачному SQL. Проблема в том, что он не может подключиться к внешнему API.
Я даже добавил исходящее правило в брандмауэр, чтобы разрешить весь трафик, но безуспешно. Позже я попытался включить журналы для брандмауэра, но журналы не создавались.
У кого-нибудь есть какие-либо идеи о том, в чем может быть проблема здесь ?
Пожалуйста, ознакомьтесь с журналами ниже и исключениями
2021-09-30T14:33:06.955652Z2021-09-30 14:33:06.955 WARN 1 --- [nio-8080-exec-3] io.netty.util.internal.MacAddressUtil :
Failed to find a usable hardware address from the network interfaces; using random bytes: 61:71:ba:a4:d5:85:ee:11
021-09-30T14:33:08.094246Z2021-09-30 14:33:08.093 DEBUG 1 --- [or-http-epoll-2] r.netty.http.client.HttpClientConnect : [id:93c6dc9b-1, L:/169.... - R:www.homedepot.ca/23.211.51.167:443] Handler is being applied: {uri=https://www.homedepot.ca/ method=GET}
2021-09-30T15:13:41.887524Z org.springframework.web.reactive.function.client.WebClientRequestException: handshake timed out after 10000ms;
nested exception is io.netty.handler.ssl.SslHandshakeTimeoutException: handshake timed out after 10000ms at
org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
2021-09-30T15:13:41.887759Z at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:414)
2021-09-30T15:13:41.887776Z at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:251)
2021-09-30T15:13:41.887805Z at reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:491)
2021-09-30T15:13:41.887813Z at reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:299)
2021-09-30T15:13:41.887822Z at reactor.core.publisher.SinkManySerialized.tryEmitNext(SinkManySerialized.java:100)
2021-09-30T15:13:41.887859Z at reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27)
2021-09-30T15:13:41.887873Z at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onError(FluxRetryWhen.java:190)
2021-09-30T15:13:41.887893Z at reactor.core.publisher.MonoCreate$MonoSink.error(MonoCreate.java:189)
2021-09-30T15:13:41.887903Z at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect$ClientTransportSubscriber.onError(HttpClientConnect.java:304)
2021-09-30T15:13:41.887916Z at reactor.core.publisher.MonoCreate$MonoSink.error(MonoCreate.java:189)
2021-09-30T15:13:41.887934Z at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.onUncaughtException(PooledConnectionProvider.java:218)
2021-09-30T15:13:41.887958Z at reactor.netty.resources.PooledConnectionProvider$PooledConnection.onUncaughtException(PooledConnectionProvider.java:467)
2021-09-30T15:13:41.887979Z at reactor.netty.channel.ChannelOperationsHandler.exceptionCaught(ChannelOperationsHandler.java:129)
2021-09-30T15:13:41.887993Z at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:302)
2021-09-30T15:13:41.888011Z at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:281)
2021-09-30T15:13:41.888032Z at io.netty.channel.AbstractChannelHandlerContext.fireExceptionCaught(AbstractChannelHandlerContext.java:273)
2021-09-30T15:13:41.888049Z at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireExceptionCaught(CombinedChannelDuplexHandler.java:424)
2021-09-30T15:13:41.982305Z at io.netty.channel.ChannelHandlerAdapter.exceptionCaught(ChannelHandlerAdapter.java:92)
2021-09-30T15:13:41.982894Z2021-09-30 15:13:41.281 WARN 1 --- [or-http-epoll-3] r.netty.http.client.HttpClientConnect : [id:c351aec9, L:/169.... - R:www.homedepot.ca/96.7.86.161:443] The connection observed an error
Код в весенней загрузке, который выполняет вызов
WebClient webClient =
WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
Mono<HDCanadaProdResp> webClientResponse =
webClient.get().uri(stockApiurl).retrieve().bodyToMono(HDCanadaProdResp.class);
webClientResponse.block();
Весь код работает в локальной системе в контейнере Docker, не знаю, в чем проблема при запуске в облаке.
Вот файл Dockerfile
FROM maven:3.6.3-openjdk-11-slim as builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src/ /app/src/
RUN mvn package -DskipTests
FROM openjdk:11-jre-slim
COPY --from=builder /app/target/*.jar /app.jar
#PORT for cloud run, coz cloud maps to this port
ENV PORT 8080
ENV HOST 0.0.0.0
CMD ["java", "-jar", "/app.jar"]
Обновление от 7 ноября 2021 года
Я изменил свой код загрузки spring, в котором я использовал API WebClient для выполнения вызова rest, и теперь ошибка изменилась.
старый код
WebClient webClient =
WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br")
.defaultHeader(HttpHeaders.ACCEPT_LANGUAGE, "en-CA,en-US;q=0.7,en;q=0.3")
.defaultHeader(HttpHeaders.HOST, "www.homedepot.ca")
.build();
Mono<HDCanadaRecomProdResp> webClientResponse =
webClient.get().uri(prodApiurl).retrieve().bodyToMono(HDCanadaRecomProdResp.class);
return webClientResponse.block();
New code
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setConnectTimeout(60 * 1000);
requestFactory.setReadTimeout(60 * 1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
ResponseEntity<HDCanadaRecomProdResp> response = null;
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br");
headers.set(HttpHeaders.ACCEPT_LANGUAGE, "en-CA,en-US;q=0.7,en;q=0.3");
headers.set(HttpHeaders.HOST, "www.homedepot.ca");
headers.setAccept(Arrays.asList(MediaType.ALL));
HttpEntity<Map<String, String>> entity = new HttpEntity<>(headers);
response =
restTemplate.exchange(prodApiurl, HttpMethod.GET, entity, HDCanadaRecomProdResp.class);
}
И теперь ошибка изменилась на
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://www.homedepot.ca/api/rec/v1/recommendations/fbt/products/1001200972/store/7301":
Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:785) at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711) at
org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602) at
com.pricetracker.productfetcher.client.HomeDepotCAClient.getProductDetails(HomeDepotCAClient.java:68) at
com.pricetracker.productfetcher.client.HomeDepotCAClient.getProductDetails(HomeDepotCAClient.java:24) at
com.pricetracker.productfetcher.service.ProductSearchServiceImpl.getProductFromWS(ProductSearchServiceImpl.java:116) at
com.pricetracker.productfetcher.service.ProductSearchServiceImpl.getProduct(ProductSearchServiceImpl.java:82) at
com.pricetracker.productfetcher.service.ProductSearchServiceImpl.productSearch(ProductSearchServiceImpl.java:61) at
com.pricetracker.productfetcher.controller.ProductsController.searchProduct(ProductsController.java:51) at
com.pricetracker.productfetcher.controller.ProductsController$FastClassBySpringCGLIB$37eced0e.invoke(<generated>) at
org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at
org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123) at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) at
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) at
com.pricetracker.productfetcher.controller.ProductsController$EnhancerBySpringCGLIB$ec439be9.searchProduct(<generated>) at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at
java.base/java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) at
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) at
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) at
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1064) at
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at
Комментарии:
1. Любопытна предпоследняя строка журнала…. «не удалось найти подходящий аппаратный адрес» любопытно. Если сервер не может определить IP-адрес хост-машины, исходящие вызовы завершатся ошибкой (поскольку невозможно вернуть ответ).
2. Включение изображений в stackoverflow не рекомендуется по нескольким причинам, в том числе из-за того, что другим трудно ссылаться на контент. Пожалуйста, копируйте и вставляйте текст, а не включайте изображения, когда это возможно.
3. В этом случае правила брандмауэра не применяются к облачным службам.
4. @DazWilkin : Я удалил изображение журналов и добавил текст.
5. @PriyashreeBhadra Я путешествовал, поэтому не мог попробовать. Но я посмотрел на Облачный Антос, мне нужно настроить его сейчас, и я бы сказал, что это будет другой путь, чем облачный запуск.
Ответ №1:
Используйте strace для такого рода проблем, связанных с запуском облака. Также просмотрите журналы в разделе «Ведение журнала в облаке» консоли GCP (не на вкладке «Журналы» раздела «Запуск в облаке»), вы можете найти «Изолированную среду контейнеров» с уровнем серьезности «ОТЛАДКА» в журналах varlog/system. Также попробуйте запустить приложение локально в Docker, используя эти инструкции, и проверьте, нормально ли загружаются ваши приложения локально. Контейнер должен прослушивать запросы на 0.0.0.0 в порту, на который отправляются запросы. По умолчанию запросы отправляются на номер 8080. Добавьте —min-экземпляры в cloudbuild.файл yaml и дайте ему некоторое значение на данный момент, а затем попробуйте.
Если вы видите этот журнал (который ранее не был виден, так как он появлялся во время запуска контейнера) Debug 2021-11-07T12:39:14.876726806ZContainer Sandbox: Unsupported syscall setsockopt(0xa,0x11,0x67,0x3ed0ac62bffc,0x4,0x1a)
и вы можете запустить установку локально, это означает, что с образом docker или вашими конфигурациями проблем нет. Unsupported syscall-
Это сообщение указывает, что системные вызовы могут не поддерживаться в этом облачном запуске (полностью управляемом), поскольку экземпляры контейнеров изолированы с помощью изолированной среды выполнения контейнеров gVisor.
Дополнительные сведения см. в руководстве по сокету(7) — Linux на странице 8. Таким образом, gvisor не поддерживает этот параметр сокета. Взгляните на эту проблему на Github, в которой говорится о том же сообщении об ошибке, которое вы получили.
Это известная проблема в полностью управляемом облачном запуске. Вы можете попробовать облачный запуск для Anthos с теми же конфигурациями и настройками контейнера (с меньшим количеством выделенных ресурсов).