diff --git a/.env.example b/.env.example index 2f5da66..f861a0e 100644 --- a/.env.example +++ b/.env.example @@ -8,4 +8,8 @@ RABBITMQ_PASSWORD=your_password MAIL_HOST=smtp.gmail.com MAIL_PORT=587 MAIL_USERNAME=your_mail -MAIL_PASSWORD=your_mail_password \ No newline at end of file +MAIL_PASSWORD=your_mail_password + +# Tracing +OTLP_TRACING_ENABLED=false +OTLP_TRACING_ENDPOINT=http://localhost:4318/v1/traces \ No newline at end of file diff --git a/build.gradle b/build.gradle index 0fc49e9..045ca3e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,84 +1,90 @@ plugins { - id 'java' - id 'org.springframework.boot' version '4.0.6' - id 'io.spring.dependency-management' version '1.1.7' - id 'com.google.cloud.tools.jib' version '3.5.3' - id 'checkstyle' + id 'java' + id 'org.springframework.boot' version '4.0.6' + id 'io.spring.dependency-management' version '1.1.7' + id 'com.google.cloud.tools.jib' version '3.5.3' + id 'checkstyle' } group = 'ditda' version = '0.0.1-SNAPSHOT' java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - // web - implementation 'org.springframework.boot:spring-boot-starter-webmvc' + // web + implementation 'org.springframework.boot:spring-boot-starter-webmvc' - // rabbitMQ - implementation 'org.springframework.boot:spring-boot-starter-amqp' + // rabbitMQ + implementation 'org.springframework.boot:spring-boot-starter-amqp' - // email - implementation 'org.springframework.boot:spring-boot-starter-mail' - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + // email + implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - // actuator - implementation 'org.springframework.boot:spring-boot-starter-actuator' + // monitoring + implementation 'org.springframework.boot:spring-boot-starter-actuator' + runtimeOnly 'io.micrometer:micrometer-registry-prometheus' - // lombok - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - testCompileOnly 'org.projectlombok:lombok' - testAnnotationProcessor 'org.projectlombok:lombok' + // tracing + implementation 'org.springframework.boot:spring-boot-micrometer-tracing-opentelemetry' + implementation 'io.micrometer:micrometer-tracing-bridge-otel' + implementation 'io.opentelemetry:opentelemetry-exporter-otlp' - // test - testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' - testImplementation 'org.springframework.boot:spring-boot-starter-amqp-test' - testImplementation 'org.springframework.boot:spring-boot-starter-thymeleaf-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + // lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + + // test + testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' + testImplementation 'org.springframework.boot:spring-boot-starter-amqp-test' + testImplementation 'org.springframework.boot:spring-boot-starter-thymeleaf-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } // CheckStyle checkstyle { - toolVersion = '13.4.1' - configFile = file("${rootDir}/config/checkstyle/checkstyle-rules.xml") - configProperties = [ - 'suppressionFile': "${rootDir}/config/checkstyle/checkstyle-suppressions.xml" - ] - maxWarnings = 0 + toolVersion = '13.4.1' + configFile = file("${rootDir}/config/checkstyle/checkstyle-rules.xml") + configProperties = [ + 'suppressionFile': "${rootDir}/config/checkstyle/checkstyle-suppressions.xml" + ] + maxWarnings = 0 } // Jib jib { - from { - image = 'eclipse-temurin:21-jre-alpine' - platforms { - platform { - architecture = 'amd64' - os = 'linux' - } - } - } - container { - ports = ['8080'] - environment = [ - 'TZ': 'Asia/Seoul' - ] - jvmFlags = ['-Xms256m', '-Xmx256m', '-XX:+UseG1GC'] - creationTime = 'USE_CURRENT_TIMESTAMP' - } + from { + image = 'eclipse-temurin:21-jre-alpine' + platforms { + platform { + architecture = 'amd64' + os = 'linux' + } + } + } + container { + ports = ['8080'] + environment = [ + 'TZ': 'Asia/Seoul' + ] + jvmFlags = ['-Xms256m', '-Xmx256m', '-XX:+UseG1GC'] + creationTime = 'USE_CURRENT_TIMESTAMP' + } } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } \ No newline at end of file diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml new file mode 100644 index 0000000..8900b38 --- /dev/null +++ b/docker-compose.prod.yaml @@ -0,0 +1,18 @@ +services: + ditda-notification-worker: + image: ${ECR_REGISTRY}:${IMAGE_TAG:-latest} + container_name: ditda-notification-worker + restart: unless-stopped + labels: + ditda.logs.app: "ditda" + ditda.logs.service: "ditda-notification-worker" + ports: + - "127.0.0.1:8083:8080" + env_file: .env + networks: + - ditda-net + + +networks: + ditda-net: + external: true \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..3fc4f47 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +services: + mailpit: + image: axllent/mailpit:v1.21 + container_name: ditda-mailpit + ports: + - "127.0.0.1:1025:1025" # SMTP + - "127.0.0.1:8025:8025" # 웹 UI + environment: + MP_SMTP_AUTH_ACCEPT_ANY: 1 + MP_SMTP_AUTH_ALLOW_INSECURE: 1 \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index f21eb7f..3bdd007 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -60,6 +60,10 @@ REQUIRED_VARS=( "MAIL_PORT" "MAIL_USERNAME" "MAIL_PASSWORD" + +# Tracing +"OTLP_TRACING_ENABLED" +"OTLP_TRACING_ENDPOINT" ) MISSING=() @@ -81,14 +85,7 @@ echo " 필수 환경변수 ${#REQUIRED_VARS[@]}개 모두 존재 확인" # === 컨테이너 교체 === echo "[4/5] 컨테이너 교체" -docker rm -f ditda-notification-worker 2>/dev/null || true -docker run -d --name ditda-notification-worker \ - --network ditda-net \ - --restart unless-stopped \ - --env-file .env \ - -p 127.0.0.1:8083:8080 \ - "${ECR_REGISTRY}:${IMAGE_TAG}" - +docker compose -f docker-compose.prod.yaml up -d # === Health Check === echo "[5/5] Health Check" diff --git a/src/main/java/ditda/notification/config/RabbitConfig.java b/src/main/java/ditda/notification/config/RabbitConfig.java index db50920..0bf9b12 100644 --- a/src/main/java/ditda/notification/config/RabbitConfig.java +++ b/src/main/java/ditda/notification/config/RabbitConfig.java @@ -1,8 +1,11 @@ package ditda.notification.config; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.support.converter.JacksonJavaTypeMapper; import org.springframework.amqp.support.converter.JacksonJsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.boot.amqp.autoconfigure.SimpleRabbitListenerContainerFactoryConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -16,4 +19,19 @@ public MessageConverter messageConverter() { return converter; } + + @Bean + public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory( + SimpleRabbitListenerContainerFactoryConfigurer configurer, + ConnectionFactory connectionFactory, + MessageConverter messageConverter + ) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + + configurer.configure(factory, connectionFactory); + factory.setMessageConverter(messageConverter); + factory.setObservationEnabled(true); + + return factory; + } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 677d1da..350edb7 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,6 @@ spring: application: - name: notification-worker + name: ditda-notification-worker profiles: active: ${SPRING_PROFILES_ACTIVE:local} @@ -36,3 +36,35 @@ spring: connectiontimeout: 5000 timeout: 10000 writetimeout: 10000 + +management: + endpoints: + web: + exposure: + include: health, prometheus + + endpoint: + health: + show-details: never + + otlp: + metrics: + export: + enabled: false + + opentelemetry: + tracing: + export: + otlp: + endpoint: ${OTLP_TRACING_ENDPOINT:http://localhost:4318/v1/traces} + + tracing: + sampling: + probability: 1.0 + export: + otlp: + enabled: ${OTLP_TRACING_ENABLED} + + metrics: + tags: + application: ${spring.application.name} \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..23cc779 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + + + + + + + logstash + + + + + 512 + 0 + + + + + + \ No newline at end of file diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index d505587..9505946 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -7,4 +7,10 @@ spring: mail: host: localhost username: test - password: test \ No newline at end of file + password: test + +management: + tracing: + export: + otlp: + enabled: false \ No newline at end of file