# syntax=docker/dockerfile:1 # 1. Сборка Angular FROM node:24-alpine AS ui WORKDIR /ui # Сначала только манифесты — слой с зависимостями пересобирается лишь при их изменении. # npm install, а не npm ci: сборка не должна падать, если package-lock.json разошёлся с package.json COPY spring-angular-ui/package*.json ./ RUN --mount=type=cache,target=/root/.npm \ npm install --legacy-peer-deps --no-audit --no-fund COPY spring-angular-ui/ ./ RUN npx ng build # 2. Сборка jar FROM eclipse-temurin:26-jdk-jammy AS build WORKDIR /app # Сначала Gradle-обвязка — зависимости скачиваются заново лишь при изменении build.gradle COPY gradlew settings.gradle build.gradle ./ COPY gradle ./gradle RUN chmod +x gradlew RUN --mount=type=cache,target=/root/.gradle \ ./gradlew dependencies --no-daemon -q > /dev/null COPY src ./src # Свежесобранный фронт кладём туда же, куда его копирует задача copyAngularBuild COPY --from=ui /ui/static/browser ./src/main/resources/static RUN --mount=type=cache,target=/root/.gradle \ ./gradlew bootJar -x test --no-daemon \ && cp "$(ls build/libs/*.jar | grep -v -- '-plain.jar' | head -n 1)" app.jar # Раскладываем jar по слоям: зависимости меняются редко и кэшируются отдельно от кода RUN java -Djarmode=tools -jar app.jar extract --layers --launcher --destination extracted # 3. Запуск FROM eclipse-temurin:26-jre-jammy # curl нужен для healthcheck RUN apt-get update \ && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/* # Непривилегированный пользователь; InitDataService пишет начальные данные в /jambotron_data RUN groupadd --system app \ && useradd --system --gid app --home-dir /app app \ && mkdir -p /jambotron_data \ && chown app:app /jambotron_data WORKDIR /app COPY --from=build --chown=app:app /app/extracted/dependencies/ ./ COPY --from=build --chown=app:app /app/extracted/spring-boot-loader/ ./ COPY --from=build --chown=app:app /app/extracted/snapshot-dependencies/ ./ COPY --from=build --chown=app:app /app/extracted/application/ ./ USER app EXPOSE 8080 HEALTHCHECK --interval=15s --timeout=5s --start-period=60s --retries=5 \ CMD curl -fs http://localhost:8080/actuator/health || exit 1 # JVM ориентируется на лимит памяти контейнера, а не на память хоста ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "org.springframework.boot.loader.launch.JarLauncher"]