Установка и проблема
У нас есть архитектура микросервисов, в которой каждая служба имеет свой отдельный экземпляр облачного SQL (как и должно быть). Эти базы данных доступны только внутри сети виртуального частного облака (VPC), и хранилище Hashicorp используется для создания динамических учетных данных для них. Нам нужно было хранилище данных для анализа и отчетности. Однако настройка немного усложнила задачу, так как мы не хотели использовать внешнюю службу ETL или иметь учетные данные базы данных с длительным сроком службы. Мы договорились, что данные необходимо извлечь в BigQuery Набор данных, из которого мы бы выставили его с помощью Метабаза
Извлечение должно происходить постепенно и периодически, поэтому службе потребуется:
1) Подключитесь к хранилищу, чтобы сгенерировать учетные данные только для чтения для каждой базы данных. 2) Прочитайте необходимые таблицы и данные. Применяйте любые преобразования, такие как преобразование типов, анонимизация или псевдонимизация. 3) Запись в набор данных BigQuery.
Поскольку данные могут быть огромными, я решил использовать Поток данных , который является сервисом потоковой и пакетной обработки Google. Поток данных построен на Apache Beam модели программирования и выполнения, которая ожидает, что вы определите Конвейер
, который соединяет источник
и приемник
данных и позволяет применять любые преобразования
или логику обработки между потоком данных. Затем он выполняет задание Конвейер
над парком вычислительных экземпляров с автоматическим масштабированием.
Решение
Мое решение – это служба загрузки Spring (с любовью называемая data-extractor
), которая будет периодически запускаться и создавать необходимые конвейеры потоков данных. Я использовал DSL Kotlin от Gradle для управления зависимостями.
Микросервису потребуются разрешения на доступ к следующим сервисам Google – BigQuery, хранилище Google (используется для хранения временных файлов), Поток данных, экземпляры вычислений. Итак, я создал специальную учетную запись службы с необходимыми разрешениями. Учетные записи служб – это особый тип учетной записи, используемой приложением или экземпляром виртуальной машины (VM), а не человеком.
Я создал службу и протестировал несколько запусков, которые все были успешными, когда я запускал службу из IDE или когда я запускал ./gradlew
из командной строки.
Служба успешно загрузится, подключится к хранилищу, чтобы получить учетные данные, создаст задание потока данных, которое определит Конвейер
и создаст необходимые вычислительные экземпляры, которые будут обрабатывать данные, а затем завершат их после выполнения задания ETL.
На всю работу ушло примерно 5 минут. Пользовательский интерфейс консоли Google показывает красивый график всего потока данных с несколькими пользовательскими и внутренними этапами, а также содержит журналы и показатели для каждого шага.
В чем проблема:
Мы развертываем все наши микросервисы в движке Google Kubernetes (GKE), и поэтому, как только сервис был в хорошей форме, я выполнил обычный процесс создания uber fat-jar и его настройки. Вот файл Dockerfile, который я скопировал из одного из наших других репозиториев Spring Boot.
FROM gcr.io/distroless/java:11 VOLUME /tmp COPY build/libs/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dspring.profiles.active=${ENV_SPRING_PROFILE}", "-jar", "/app.jar"]
Предполагая, что ./gradlew сборка
была успешно запущена ранее, она копирует файл fat-jar внутри изображения, а затем запускает его при запуске контейнера. Довольно стандартно, не так ли?
Вот тут-то все и начало рушиться. Когда я запустил контейнер (локально, а также в GKE), я заметил, что служба успешно загрузится, подключится к хранилищу для считывания учетных данных, создаст задание потока данных, запустит экземпляр, но задание просто застрянет. В журналах просто говорилось бы, что
Проверка разрешений, предоставленных учетной записи службы контроллера.
Журнал этого будет печататься каждые 5-6 минут в течение часа, после чего задание завершится неудачей.
Здесь начался болезненный процесс отладки. Я отчаянно искал какие-либо известные ошибки или проблемы, соответствующие ответы на StackOverflow или что-нибудь, что я мог найти в документах Google, но ничего не смог найти.
Задание будет успешно работать в среде IDE или с помощью Gradle, но завершится неудачей при настройке .
Это случалось 10/10 раз или, скорее, сотни раз, когда я пытался.
Журналы намекали на возможную проблему с разрешениями для учетной записи службы. Однако это не имело полного смысла, поскольку выполнение было успешным из среды IDE или при запуске с помощью Gradle. Я убедился, что в моей локальной среде не было других учетных данных Google, и учетная запись службы была единственной учетной записью, введенной в качестве переменной среды. Все переменные среды были одинаковыми, за исключением части контейнеризации.
Я включил журналы отладки
, но это также не дало никакой полезной информации. Задание застряло бы с той же ошибкой. Консоль Google отобразила неудачное задание с сообщением:
Сбой рабочего процесса. Причины: Задание потока данных, похоже, застряло, потому что за последние 1 час не было замечено никакой активности работника. Пожалуйста, проверьте рабочие журналы в журнале драйверов стека. Вы также можете получить помощь с облачным потоком данных по адресу https://cloud.google.com/dataflow/support .
Расстроенный, я перепробовал все, начиная от подключения к компьютеру и заканчивая проверкой сетевого подключения и чтением системных журналов, но нигде не было никакой подсказки. Я даже открыл обращение в службу поддержки Google из нашей учетной записи, но не получил полезного ответа. В основном потому, что они не могли найти ничего плохого на своей стороне.
Во время выступления я пошутил по этому поводу, что мне, возможно, придется развернуть свой ноутбук.
В перерывах между другими задачами я потратил на это 2 дня – все больше и больше расстраиваясь и уставая.
Эксперимент
Работая в сжатые сроки, я решил закрепить весь свой репозиторий и запустить /gradlew run
изнутри него. Создание банки с жиром и ее докеризация
Это не должно сработать. Верно?
К моему удивлению, так оно и было.
Теперь это сработало 10/10 раз. Я был ошеломлен.
Теперь я был еще более озадачен, чем раньше, но в то же время испытал облегчение оттого, что меня разблокировали. Я потратил впустую 2 дня, застряв на этой проблеме – проблеме, которую я не понял, не смог развернуть приложение. Это были 2 дня моей жизни, которые я не собирался возвращать.
Оказывается, есть что-то в сгенерированном fat-jar
из ./gradlew сборка
, из-за которой он не может работать с потоком данных. Я проверил это, также запустив fat jar напрямую из командной строки:
# data-extractor.jar is the fat uber jar containing all dependencies. java -jar data-extractor.jar
Он вышел из строя, как контейнер Docker, и в той же точке.
Проверка разрешений, предоставленных учетной записи службы контроллера.
Это было дикое путешествие. Я решил, что буду развертывать то, что работает параллельно, я пытаюсь понять и понять, что происходит.
Вот мой обновленный Dockerfile
, который копирует код в контейнер и запускает классы gradlew
для извлечения зависимостей и компиляции кода.
FROM adoptopenjdk:11-jdk-hotspot VOLUME /app/data-extractor WORKDIR /app/data-extractor # Copy source code COPY src /app/data-extractor/src # Copy gradle configuration and runner COPY *.gradle.kts /app/data-extractor/ COPY gradlew /app/data-extractor/ COPY gradle /app/data-extractor/gradle # This will install the dependencies in the image RUN ./gradlew --no-daemon classes EXPOSE 8080 ENTRYPOINT ["./gradlew" "--no-daemon" "run"]
Я не знаю, почему здесь не работает банка с жиром. Это первый раз, когда я столкнулся с этим, и я хотел бы лучше понять. Возможно, кто-то еще в сообществе знает почему и поделится этим.
- Есть ли еще такие случаи?
- Как люди работают вокруг них?
- Есть ли что-то, что я могу сделать лучше?
Поделиться этим, чтобы кто-то другой, работающий над чем-то подобным, мог сэкономить время и разочарование.
Вывод
- Компьютеры и современное программное обеспечение сложны и сложны. Во всем подвиге интеграции сочетания сложных технологий, таких как Vault, Kubernetes, BigQuery, поток данных и сама логика сервиса, эта мелочь причинила мне наибольшую боль. Части, которые, как вам кажется, вы хорошо понимаете, могут неожиданно выйти из строя при совместной работе.
- В облачной среде с несколькими движущимися частями журналы иногда могут вводить в заблуждение. Когда это происходит, важно тщательно проверять даже самые мелкие вещи. Большую часть времени я подозревал, что что-то связано с разрешениями, и тщательно проверял роли и разрешения. Помогает сделать перерыв и посмотреть в другом направлении.
- Я уверен, что этому есть вполне разумное объяснение. Может быть, это даже задокументировано. Но я не мог легко найти его, и ошибка не была очевидной. Обмен таким опытом – хороший способ учиться друг у друга, не совершая одних и тех же ошибок. Я узнал и смирился с тем, что я не знаю всего, и это прекрасно.
Если вы уже сделали это, пожалуйста, дайте мне знать, что вы думаете.
Первоначально опубликовано на https://suspendfun.com
Оригинал: “https://dev.to/therajsaxena/dataflow-springboot-app-fails-to-run-when-dockerized-1fo5”