Spring Boot в AWS: Готово за продукция ръководство
Spring Boot в продукция в AWS: слоести Docker образи, JVM настройки за контейнер, ECS или EKS, плавно спиране, мащабиране и контрол на разходите.
Въведение
Внедряването на Spring Boot приложение в AWS означава доста повече от това да качите JAR файл на EC2 инстанция. Готовото за продукция внедряване изисква внимателно обмисляне на контейнеризацията, оркестрацията, мониторинга, сигурността и оптимизацията на разходите.
В това ръководство споделяме практиките, които изградихме през годините работа с корпоративни Spring Boot приложения в AWS, включително платформата Olympus Mobility.
Контейнеризация с Docker
Започнете с многоетапен Dockerfile, който произвежда минимален и сигурен образ. Използвайте Eclipse Temurin за базов образ и се възползвайте от поддръжката на Spring Boot за слоести JAR файлове, за да ускорите билдовете.
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build
COPY . .
RUN ./mvnw -q -DskipTests package && \
java -Djarmode=layertools -jar target/app.jar extract
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=builder /build/dependencies/ ./
COPY --from=builder /build/spring-boot-loader/ ./
COPY --from=builder /build/application/ ./
USER app
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
Редът на слоевете има значение: зависимостите се променят рядко, а класовете на приложението - при всеки билд. Разделянето им в отделни слоеве означава, че промяна само в кода качва килобайти, а не целия артефакт.
Дръжте образите малки - нашите продукционни образи обикновено са под 200MB. Пускайте процеса с потребител без root права, както е по-горе; не струва нищо, а е първото нещо, за което ще попитат при преглед на сигурността.
JVM настройките, които наистина имат значение в контейнер
Най-честата грешка в продукция, която срещаме, е JVM, която не знае, че работи в контейнер.
JAVA_TOOL_OPTIONS=-XX:MaxRAMPercentage=75 -XX:+UseG1GC -XX:+ExitOnOutOfMemoryError
Използвайте MaxRAMPercentage вместо фиксиран -Xmx, за да следва heap-ът размера на задачата, вместо да го пренастройвате при всяка промяна. Оставете запас - JVM има нужда от памет за metaspace, стековете на нишките и direct буферите, а heap, оразмерен на 100% от лимита на контейнера, води до това процесът да бъде убит от системата, вместо да получите диагностицируема Java грешка. ExitOnOutOfMemoryError също е важен: мъртъв контейнер бива заменен, докато куцащ след OOM просто проваля проверките за здраве през ден.
ECS или EKS: избор на оркестрация
За повечето Spring Boot приложения Amazon ECS с Fargate е правилният избор. Управлява се по-лесно от EKS, а Fargate премахва изцяло нуждата да поддържате EC2 инстанции.
Използваме EKS само когато ни трябва по-сложно планиране, собствени оператори или когато екипът вече има опит с Kubernetes.
Честният прочит е следният: EKS си струва, когато уменията за Kubernetes вече са в екипа. Ако ги няма, ECS ви извежда в продукция по-рано и клъстерът престава да е нещо, което трябва да поддържате.
Проверки за здраве и плавно спиране
Spring Boot Actuator предоставя две различни проби и балансьорът на натоварване има нужда и от двете:
management.endpoint.health.probes.enabled=true
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
Насочете target групата на ALB към /actuator/health/readiness, а не към /actuator/health. Liveness отговаря на въпроса „счупен ли е процесът“, а readiness - на „трябва ли тази инстанция да поема трафик“. Вържете ли балансьора за liveness, инстанциите започват да поемат заявки, преди пулът от връзки да е загрят.
Задайте stopTimeout на ECS задачата по-голям от времето за плавно спиране. Ако ECS изпрати SIGKILL след 30 секунди, докато Spring още изчаква своите 30, при всяко внедряване губите заявки в движение.
Стратегия за автоматично мащабиране
Настройте политики за мащабиране с проследяване на цел, базирани на използването на процесор и памет. За Spring Boot приложения обикновено задаваме:
- Цел за процесор: 60-70%
- Цел за памет: 70-80%
- Минимален брой задачи: 2 (за висока достъпност)
- Изчакване при намаляване: 300 секунди
Имайте предвид, че използваната от JVM памет сама по себе си е слаб сигнал за мащабиране - здравият heap стои близо до тавана си по замисъл, защото колекторът няма причина да се задейства по-рано. Процесорът обикновено е по-честният показател, а броят заявки на инстанция е добър втори.
Съображения за базата данни
Използвайте Amazon RDS за PostgreSQL с Multi-AZ внедряване в продукция. Ключови настройки:
- Включете Performance Insights за анализ на заявките
- Настройте пул от връзки с HikariCP
- Настройте автоматични резервни копия с подходящ срок на съхранение
- Използвайте read реплики за натоварвания с интензивно четене
За размера на пула - устоявайте на изкушението да го правите голям. Общият брой връзки от всички задачи трябва да остане под лимита на инстанцията, тоест пул от 10 при 20 автоматично мащабирани задачи вече прави 200 връзки. Малките пулове с кратък connectionTimeout се провалят бързо и се възстановяват; големите основно преместват опашката от приложението в базата.
Тайни и конфигурация
Дръжте тайните извън дефиницията на задачата. Реферирайте AWS Secrets Manager или SSM Parameter Store по ARN, за да се подават стойностите при стартиране на задачата и да не се появяват нито в CloudFormation или Terraform състоянието, нито в конзолата, нито в изхода на docker inspect. Смяната на парола тогава е операция в Secrets Manager и рестарт на задачата, а не ново внедряване.
Мониторинг и наблюдаемост
Продукционното внедряване се нуждае от трите стълба на наблюдаемостта:
- Метрики: CloudWatch със собствени метрики от Spring Boot Actuator
- Логове: централизирани в CloudWatch Logs, със структурирано JSON логване
- Trace-ове: разпределено проследяване с AWS X-Ray или OpenTelemetry
Micrometer вече е в приложението ви, така че експортът към CloudWatch е въпрос на конфигурация, а не на код. Логвайте в JSON от самото начало - въвеждането на структурирано логване в жива система след това е далеч по-досадно, отколкото да го включите в първия ден.
Безопасно внедряване
Използвайте CodeDeploy с blue/green при ECS, за да може лошо издание автоматично да върне трафика назад, вместо да разчитате някой да забележи. Комбинирайте го с аларма в CloudWatch за дела на 5xx отговорите като спусък за връщане назад и пазете миграциите по базата съвместими назад за едно издание - по време на превключването и двете версии за кратко работят срещу една и съща схема.
Оптимизация на разходите
Разходите в AWS растат бързо. Най-полезните ни съвети:
- Използвайте Savings Plans за предвидими натоварвания
- Оразмерявайте правилно Fargate задачите си
- Задайте подходящи настройки за паметта на JVM
- Използвайте S3 Intelligent-Tiering за съхранение
- Настройте аларми и бюджети за разходите
На практика двете най-големи печалби почти винаги са свиването на преоразмерените задачи и задаването на срок за съхранение на логовете - CloudWatch Logs, пазени вечно, тихомълком се превръщат в осезаемо перо.
Заключение
Добре проектираното внедряване на Spring Boot приложения в AWS носи надеждност, мащабируемост и ефективност на разходите. Започнете просто, мерете всичко и оптимизирайте на база реални данни.
Ако предпочитате това да не е ваша грижа, екипът ни за поддръжка и обслужване поема продукционни Spring Boot натоварвания в AWS.
