Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 72 additions & 62 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,62 +1,72 @@
#name: CI (ktlint auto-format & build)
#
##on: ci 잠시 무력화 ^^ TODO 모듈 맞춰서 하기
## pull_request:
## branches: [ "master" ]
## push:
## branches: [ "master" ]
#
#permissions:
# contents: write # commit/push 권한
# pull-requests: write
#
#jobs:
# build-and-format:
# runs-on: ubuntu-latest
#
# steps:
# - name: Checkout repository (with history)
# uses: actions/checkout@v3
# with:
# fetch-depth: 0
# token: ${{ secrets.GITHUB_TOKEN }}
#
# - name: Set up JDK 17
# uses: actions/setup-java@v3
# with:
# java-version: '17'
# distribution: 'temurin'
#
# - name: Ensure gradlew is executable
# run: chmod +x ./gradlew
#
# # 🔹 코드스타일 체크
# - name: Run ktlint check
# run: ./gradlew ktlintCheck || true
#
# # 🔹 코드스타일 자동 포맷
# - name: Run ktlint format
# run: ./gradlew ktlintFormat || true
#
# # 🔹 PR 브랜치일 때만 커밋/푸시
# - name: Commit and push ktlint fixes (only for PRs, not master)
# if: github.event_name == 'pull_request'
# run: |
# git config user.name "github-actions[bot]"
# git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
#
# if git diff --quiet && git diff --staged --quiet; then
# echo "No formatting changes to commit."
# else
# git add -A
# if git diff --staged --quiet; then
# echo "No staged changes."
# else
# git commit -m "chore: apply ktlintFormat (auto-format by CI)"
# git push origin HEAD:${{ github.head_ref }}
# fi
# fi
#
# # 🔹 빌드 (테스트 제외)
# - name: Build with Gradle (skip tests)
# run: ./gradlew build -x test
name: CI (ktlint for PR / selective build manual)

on:
pull_request:
branches: [ "master" ]
workflow_dispatch:
inputs:
BUILD_MODULES:
description: "Space-separated list of modules to build (e.g. core api cli)"
required: false
default: ""

permissions:
contents: write
pull-requests: write

jobs:
ktlint-or-build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Ensure gradlew executable
run: chmod +x ./gradlew

- name: Run ktlint (on Pull Request)
if: github.event_name == 'pull_request'
run: |
echo "🔹 Running ktlint for pull request..."
./gradlew ktlintCheck || true
./gradlew ktlintFormat || true

- name: Commit and push ktlint fixes (only for PRs)
if: github.event_name == 'pull_request'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git diff --quiet && git diff --staged --quiet; then
echo "No formatting changes to commit."
else
git add -A
git commit -m "chore: auto-format by ktlint (CI)"
git push origin HEAD:${{ github.head_ref }}
fi
- name: Selective build (manual trigger)
if: github.event_name == 'workflow_dispatch'
run: |
if [ -z "${{ github.event.inputs.BUILD_MODULES }}" ]; then
echo "No modules specified, skipping build."
exit 0
fi

echo "🔹 Building selected modules: ${{ github.event.inputs.BUILD_MODULES }}"
for module in ${{ github.event.inputs.BUILD_MODULES }}; do
if [ -d "$module" ]; then
echo "🚀 Building module: $module"
./gradlew :"$module":build -x test
else
echo "⚠️ Module '$module' not found, skipping."
fi
done
3 changes: 1 addition & 2 deletions ai/src/main/kotlin/lab/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication(
scanBasePackages = ["lab.api", "lab.`ai-model`"]
scanBasePackages = ["lab.api", "lab.`ai-model`"],
)
class Application

fun main(args: Array<String>) {
runApplication<Application>(*args)
}

7 changes: 5 additions & 2 deletions ai/src/main/kotlin/lab/ai-model/NormalizationUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ object NormalizationUtil {

return features.map { f ->
DoubleArray(numFeatures) { i ->
if (maxVals[i] == minVals[i]) 0.0
else (f[i] - minVals[i]) / (maxVals[i] - minVals[i])
if (maxVals[i] == minVals[i]) {
0.0
} else {
(f[i] - minVals[i]) / (maxVals[i] - minVals[i])
}
}
}.toTypedArray()
}
Expand Down
5 changes: 2 additions & 3 deletions ai/src/main/kotlin/lab/ai-model/gc/GcAnomalyDetector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import java.io.ObjectOutputStream

@Component
class GcAnomalyDetector {

private val extractor: GcFeatureExtractor by lazy { GcFeatureExtractor }
private val normalizationUtil: NormalizationUtil by lazy { NormalizationUtil }

Expand Down Expand Up @@ -58,13 +57,13 @@ class GcAnomalyDetector {
}

private fun getDataList(isTestSet: Boolean = true): List<GcTrainData> {
return if(isTestSet) {
return if (isTestSet) {
listOf(
GcTrainData(120, 500, 40, 1.2, 400_000, "G1", label = 1),
GcTrainData(200, 800, 350, 3.8, 1_200_000, "G1", label = 1),
GcTrainData(85, 260, 12, 0.9, 250_000, "Parallel", label = 1),
GcTrainData(600, 1500, 900, 6.8, 2_200_000, "G1", label = 1),
GcTrainData(95, 340, 18, 1.4, 370_000, "Serial", label = 1)
GcTrainData(95, 340, 18, 1.4, 370_000, "Serial", label = 1),
)
} else {
// TODO heesung feature
Expand Down
16 changes: 8 additions & 8 deletions ai/src/main/kotlin/lab/ai-model/gc/GcFeatureExtractor.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package lab.`ai-model`.gc


object GcFeatureExtractor {
private val gcStrategyMap = mapOf(
"G1" to 0.0,
"Parallel" to 1.0,
"Serial" to 2.0,
"ZGC" to 3.0
)
private val gcStrategyMap =
mapOf(
"G1" to 0.0,
"Parallel" to 1.0,
"Serial" to 2.0,
"ZGC" to 3.0,
)

fun extract(gc: GcTrainData): DoubleArray {
return doubleArrayOf(
Expand All @@ -16,7 +16,7 @@ object GcFeatureExtractor {
gc.pause.toDouble(),
gc.allocationRate,
gc.liveDataSize.toDouble(),
gcStrategyMap[gc.gcStrategy] ?: -1.0
gcStrategyMap[gc.gcStrategy] ?: -1.0,
)
}
}
45 changes: 24 additions & 21 deletions ai/src/main/kotlin/lab/ai-model/gc/GcLogisticRegressionTrainer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,25 @@ class GcLogisticRegressionTrainer {
val normalizedFeatures = normalizationUtil.normalize(features)
val labels = dataList.map { it.label }.toIntArray()

val df = DataFrame.of(
DoubleVector.of("count", normalizedFeatures.map { it[0] }.toDoubleArray()),
DoubleVector.of("time", normalizedFeatures.map { it[1] }.toDoubleArray()),
DoubleVector.of("pause", normalizedFeatures.map { it[2] }.toDoubleArray()),
DoubleVector.of("allocationRate", normalizedFeatures.map { it[3] }.toDoubleArray()),
DoubleVector.of("liveDataSize", normalizedFeatures.map { it[4] }.toDoubleArray()),
DoubleVector.of("gcStrategy", normalizedFeatures.map { it[5] }.toDoubleArray()),
IntVector.of("label", labels)
)
val df =
DataFrame.of(
DoubleVector.of("count", normalizedFeatures.map { it[0] }.toDoubleArray()),
DoubleVector.of("time", normalizedFeatures.map { it[1] }.toDoubleArray()),
DoubleVector.of("pause", normalizedFeatures.map { it[2] }.toDoubleArray()),
DoubleVector.of("allocationRate", normalizedFeatures.map { it[3] }.toDoubleArray()),
DoubleVector.of("liveDataSize", normalizedFeatures.map { it[4] }.toDoubleArray()),
DoubleVector.of("gcStrategy", normalizedFeatures.map { it[5] }.toDoubleArray()),
IntVector.of("label", labels),
)

val formula = Formula.lhs("label")
val props = Properties().apply {
// 하이퍼파라미터 설정
setProperty("lambda", "1e-4")
setProperty("tol", "1e-5")
setProperty("maxIter", "500")
}
val props =
Properties().apply {
// 하이퍼파라미터 설정
setProperty("lambda", "1e-4")
setProperty("tol", "1e-5")
setProperty("maxIter", "500")
}
model = LogisticRegression.fit(formula, df, props)
log.info("GcTrainer training completed.")

Expand All @@ -73,10 +75,11 @@ class GcLogisticRegressionTrainer {
}

private fun saveModel(key: String) {
val m = model ?: run {
log.error("Model not trained. Cannot save [$key].")
return
}
val m =
model ?: run {
log.error("Model not trained. Cannot save [$key].")
return
}

val file = File(modelDir, "gc_$key.model")

Expand All @@ -88,13 +91,13 @@ class GcLogisticRegressionTrainer {
}

private fun getDataList(isTestSet: Boolean = true): List<GcTrainData> {
return if(isTestSet) {
return if (isTestSet) {
listOf(
GcTrainData(120, 500, 40, 1.2, 400_000, "G1", label = 1),
GcTrainData(200, 800, 350, 3.8, 1_200_000, "G1", label = 1),
GcTrainData(85, 260, 12, 0.9, 250_000, "Parallel", label = 1),
GcTrainData(600, 1500, 900, 6.8, 2_200_000, "G1", label = 1),
GcTrainData(95, 340, 18, 1.4, 370_000, "Serial", label = 1)
GcTrainData(95, 340, 18, 1.4, 370_000, "Serial", label = 1),
)
} else {
// TODO heesung feature
Expand Down
2 changes: 1 addition & 1 deletion ai/src/main/kotlin/lab/ai-model/gc/GcTrainData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ data class GcTrainData(
val allocationRate: Double,
val liveDataSize: Long,
val gcStrategy: String,
val label: Int // 1 정상, 0 비정상
val label: Int, // 1 정상, 0 비정상
)
7 changes: 4 additions & 3 deletions ai/src/main/kotlin/lab/api/ApiController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ class ApiController(
private val gcLogisticRegressionTrainer: GcLogisticRegressionTrainer,
private val gcAnomalyDetector: GcAnomalyDetector,
) {

@GetMapping("/api/train")
fun train(@RequestParam trainModelName: String?) {
when(trainModelName) {
fun train(
@RequestParam trainModelName: String?,
) {
when (trainModelName) {
"gc_logistic_regression" -> gcLogisticRegressionTrainer.train()
"gc_anomaly_detector" -> gcAnomalyDetector.train()
else -> throw IllegalArgumentException("Unknown model name: $trainModelName")
Expand Down
46 changes: 24 additions & 22 deletions cli/src/main/kotlin/lab/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import org.springframework.boot.WebApplicationType
import org.springframework.boot.builder.SpringApplicationBuilder

fun main(args: Array<String>) {
val context = SpringApplicationBuilder(ExporterConfig::class.java)
.web(WebApplicationType.NONE)
.bannerMode(Banner.Mode.OFF)
.logStartupInfo(false)
.run(*args)
val context =
SpringApplicationBuilder(ExporterConfig::class.java)
.web(WebApplicationType.NONE)
.bannerMode(Banner.Mode.OFF)
.logStartupInfo(false)
.run(*args)

val exporter = context.getBean(JvmExporter::class.java)

Expand All @@ -38,24 +39,25 @@ private fun loop(action: () -> Unit) {
}
}

private fun printSummary(exporter: JvmExporter) = loop {
val mem = exporter.getMemoryInfo()
val gc = exporter.getGcInfo()
val cpu = exporter.getCpuInfo()
val threads = exporter.getThreadInfo()
private fun printSummary(exporter: JvmExporter) =
loop {
val mem = exporter.getMemoryInfo()
val gc = exporter.getGcInfo()
val cpu = exporter.getCpuInfo()
val threads = exporter.getThreadInfo()

println(
"""
==== JVM Summary ====
Host: ${exporter.getHostname()}
Memory Used: ${mem.used / 1024 / 1024} MB / ${mem.max / 1024 / 1024} MB
GC Count: ${gc.count}, Time: ${gc.time} ms
CPU Usage: ${"%.2f".format(cpu.processUsage * 100)} %
Threads: ${threads.count} (Daemon: ${threads.daemonCount})
=====================
""".trimIndent()
)
}
println(
"""
==== JVM Summary ====
Host: ${exporter.getHostname()}
Memory Used: ${mem.used / 1024 / 1024} MB / ${mem.max / 1024 / 1024} MB
GC Count: ${gc.count}, Time: ${gc.time} ms
CPU Usage: ${"%.2f".format(cpu.processUsage * 100)} %
Threads: ${threads.count} (Daemon: ${threads.daemonCount})
=====================
""".trimIndent(),
)
}

private fun printGc(exporter: JvmExporter) {
val gc = exporter.getGcInfo()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RestController
class MetricController(
private val metricService: MetricService,
) {

@GetMapping("/metric")
fun getGcMetrics(): ResponseEntity<List<GcMetricResponse>> {
val data = metricService.getAllGcMetrics()
Expand Down
Loading