From 1ea8506df531d2c180dd370d46c5422c9e867b12 Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 27 Feb 2024 23:18:19 +0100 Subject: [PATCH] otus #30 --- otus-30/Dockerfile | 32 ++++++++ otus-30/Dockerfile.lambda | 21 ++++++ otus-30/Dockerfile.simple | 7 ++ otus-30/README.md | 115 +++++++++++++++++++++++++++++ otus-30/elastic.properties | 112 ++++++++++++++++++++++++++++ otus-30/entrypoint.sh | 46 ++++++++++++ otus-30/jmx-config.yaml | 2 + otus-30/logfile.log | 3 + otus-30/project.clj | 23 ++++++ otus-30/resources/logback.xml | 24 ++++++ otus-30/src/otus_30/core.clj | 39 ++++++++++ otus-30/src/otus_30/lambda.clj | 9 +++ otus-30/test/otus_30/core_test.clj | 7 ++ 13 files changed, 440 insertions(+) create mode 100644 otus-30/Dockerfile create mode 100644 otus-30/Dockerfile.lambda create mode 100644 otus-30/Dockerfile.simple create mode 100644 otus-30/README.md create mode 100644 otus-30/elastic.properties create mode 100755 otus-30/entrypoint.sh create mode 100644 otus-30/jmx-config.yaml create mode 100644 otus-30/logfile.log create mode 100644 otus-30/project.clj create mode 100644 otus-30/resources/logback.xml create mode 100644 otus-30/src/otus_30/core.clj create mode 100644 otus-30/src/otus_30/lambda.clj create mode 100644 otus-30/test/otus_30/core_test.clj diff --git a/otus-30/Dockerfile b/otus-30/Dockerfile new file mode 100644 index 0000000..8c7fe60 --- /dev/null +++ b/otus-30/Dockerfile @@ -0,0 +1,32 @@ +FROM clojure:temurin-20-lein as builder + +RUN mkdir /build + +WORKDIR /build + +COPY src src +COPY project.clj project.clj +COPY entrypoint.sh entrypoint.sh + +RUN lein uberjar + + + +FROM eclipse-temurin:20-jdk + +RUN mkdir -p /opt/jmx_exporter +RUN wget -O /opt/jmx_exporter/jmx_prometheus_javaagent-0.16.1.jar https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.16.1/jmx_prometheus_javaagent-0.16.1.jar +COPY jmx-config.yaml /opt/jmx_exporter/config.yaml + +RUN mkdir -p /opt/elastic-apm +RUN wget -O /opt/elastic-apm/elastic-apm-agent-1.34.1.jar https://search.maven.org/remotecontent?filepath=co/elastic/apm/elastic-apm-agent/1.34.1/elastic-apm-agent-1.34.1.jar +COPY elastic.properties /opt/elastic-apm/elastic.properties + +RUN mkdir /service + +COPY --from=builder /build/target/production-app.jar /service/production-app.jar +COPY --from=builder /build/entrypoint.sh /service/entrypoint.sh + +EXPOSE 8080 + +ENTRYPOINT ["./service/entrypoint.sh"] diff --git a/otus-30/Dockerfile.lambda b/otus-30/Dockerfile.lambda new file mode 100644 index 0000000..13b3587 --- /dev/null +++ b/otus-30/Dockerfile.lambda @@ -0,0 +1,21 @@ +FROM --platform=linux/amd64 clojure:openjdk-11-lein-2.9.5 as builder + +RUN mkdir /opt/app +WORKDIR /opt/app + +COPY src src +COPY project.clj project.clj + +RUN lein uberjar + + + +FROM --platform=linux/amd64 public.ecr.aws/lambda/java:11 + +RUN mkdir /opt/app + +COPY --from=builder /opt/app/target/lambda.jar /opt/app/app.jar + +ENTRYPOINT [ "java", "-cp", "/opt/app/app.jar", "com.amazonaws.services.lambda.runtime.api.client.AWSLambda" ] + +CMD ["otus-30.lambda::handleRequest"] diff --git a/otus-30/Dockerfile.simple b/otus-30/Dockerfile.simple new file mode 100644 index 0000000..ecbef5b --- /dev/null +++ b/otus-30/Dockerfile.simple @@ -0,0 +1,7 @@ +FROM openjdk:17 + +WORKDIR / + +COPY target/production-app.jar / + +CMD java -jar target/production-app.jar diff --git a/otus-30/README.md b/otus-30/README.md new file mode 100644 index 0000000..ae3af5f --- /dev/null +++ b/otus-30/README.md @@ -0,0 +1,115 @@ +# otus-30 + + +### Конфигурация для сборки в project.clj + +```clojure +:main ^:skip-aot otus-30.core + +{:uberjar {:aot :all}} +``` +Почитать про компиляцию +https://clojure.org/reference/compilation + + +### Сборка jar файла + +```shell +lein uberjar +``` +### Запуск + +```shell +java -jar target/production-app.jar +``` + + +### Docker + +Сборка Docker образа + +```shell +docker build --tag otus-clojure/app:1.1 . +``` +Запуск Docker контейнера + +```shell +docker run -p 8080:8080 otus-clojure/app +``` + + +### JVM options + +https://blogs.oracle.com/javamagazine/post/the-best-hotspot-jvm-options-and-switches-for-java-11-through-java-17 + +- -XX:InitialRAMPercentage +- -XX:MaxRAMPercentage +- -XX:+UseSerialGC +- -XX:+UseParallelGC +- -XX:+UseZGC +- -XX:+UnlockExperimentalVMOptions +- -XX:+UseContainerSupport + + +### Логирование + +Java logging frameworks +https://lambdaisland.com/blog/2020-06-12-logging-in-clojure-making-sense-of-the-mess + +Pure Clojure logging +https://github.com/BrunoBonacci/mulog + + +### Сбор JMX метрик + +https://github.com/prometheus/jmx_exporter +https://opentelemetry.io/docs/collector +https://prometheus.io + + +### Сборка проекта с помощью GraalVM + +https://github.com/graalvm/graalvm-ce-builds/releases +https://github.com/clj-easy/graalvm-clojure/tree/master/ + +```shell +lein do clean, uberjar + +native-image --report-unsupported-elements-at-runtime \ + --initialize-at-build-time \ + --no-server \ + -jar ./target/production-app.jar \ + -H:Name=./target/hello-world + +./target/hello-world +``` + +```shell +time java -jar ./target/production-app.jar + +time ./target/hello-world +``` + + +### AWS Lambda + +Required Java interface +https://github.com/aws/aws-lambda-java-libs/blob/main/aws-lambda-java-runtime-interface-client/README.md + + + +## Homework + +Применить принципы hexagonal architecture в проекте на выбор (Pokemon app) + +### Задание + +- Разделить код проекта на доменные модули +- Выделить в сервисы код взаимодействующий с внешними системами +- Подключить к проекту фреймворк Duct +- Написать конфигурации для двух режимов запуска приложения (dev, production) +- Применить dependency injection для тестирования логики приложения +- Настроить production сборку для проекта + - Создать Dockerfile для сборки и запуска + - Создать entrypoint.sh фаил + - Настроить логирование и сборку метрик (JMX) diff --git a/otus-30/elastic.properties b/otus-30/elastic.properties new file mode 100644 index 0000000..57fc3ea --- /dev/null +++ b/otus-30/elastic.properties @@ -0,0 +1,112 @@ +# https://www.elastic.co/guide/en/apm/agent/java/master/config-reference-properties-file.html + +############################################ +# Circuit-Breaker # +############################################ + +circuit_breaker_enabled=false + +############################################ +# Core # +############################################ + +recording=true +enabled=true +instrument=true +transaction_sample_rate=1 +transaction_max_spans=500 +sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,*auth*,*principal*,set-cookie +enable_experimental_instrumentations=false +capture_body=ALL +capture_headers=true +central_config=true +breakdown_metrics=true +config_file=_AGENT_HOME_/elasticapm.properties +use_elastic_traceparent_header=true +span_min_duration=0ms +cloud_provider=AWS +enable_public_api_annotation_inheritance=false +trace_continuation_strategy=CONTINUE + +############################################ +# HTTP # +############################################ + +capture_body_content_types=application/x-www-form-urlencoded*,text/*,application/json*,application/xml* +transaction_ignore_urls=/VAADIN/*,*/healthcheck,/heartbeat*,/favicon.ico,*.js,*.css,*.jpg,*.jpeg,*.png,*.gif,*.webp,*.svg,*.woff,*.woff2 +use_path_as_transaction_name=true + +############################################ +# Huge Traces # +############################################ + +span_compression_enabled=true +span_compression_exact_match_max_duration=50ms +span_compression_same_kind_max_duration=0ms +exit_span_min_duration=0ms + +############################################ +# JAX-RS # +############################################ + +enable_jaxrs_annotation_inheritance=false +use_jaxrs_path_as_transaction_name=false + +############################################ +# JMX # +############################################ + +capture_jmx_metrics=\ +object_name[java.lang:type=Memory] \ +attribute[HeapMemoryUsage:metric_name=heap] \ +attribute[NonHeapMemoryUsage:metric_name=nonheap] \ +, \ +object_name[java.lang:type=Threading] \ +attribute[ThreadCount:metric_name=thread_count] + +############################################ +# Logging # +############################################ + +log_level=INFO +log_file=System.out +log_ecs_reformatting=OFF +log_format_file=JSON + +############################################ +# Metrics # +############################################ + +dedot_custom_metrics=true +metric_set_limit=1000 + +############################################ +# Profiling # +############################################ + +profiling_inferred_spans_enabled=false +profiling_inferred_spans_sampling_interval=50ms +profiling_inferred_spans_min_duration=0ms +profiling_inferred_spans_included_classes=* +profiling_inferred_spans_excluded_classes=(?-i)java.*,(?-i)javax.*,(?-i)sun.*,(?-i)com.sun.*,(?-i)jdk.*,(?-i)org.apache.tomcat.*,(?-i)org.apache.catalina.*,(?-i)org.apache.coyote.*,(?-i)org.jboss.as.*,(?-i)org.glassfish.*,(?-i)org.eclipse.jetty.*,(?-i)com.ibm.websphere.*,(?-i)io.undertow.* + +############################################ +# Reporter # +############################################ + +disable_send=false +server_timeout=5s +verify_server_cert=true +max_queue_size=512 +include_process_args=false +api_request_time=10s +api_request_size=768kb +metrics_interval=30s + +############################################ +# Stacktrace # +############################################ + +application_packages=otus-30.core +stack_trace_limit=50 +span_stack_trace_min_duration=0ms diff --git a/otus-30/entrypoint.sh b/otus-30/entrypoint.sh new file mode 100755 index 0000000..89a8691 --- /dev/null +++ b/otus-30/entrypoint.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +APP_NAME=otus-app +JAR_PATH=/service/production-app.jar +ENVIRONMENT=dev + +# Sets initial values of variables +PWD="$(dirname "$0")" +LOG_DIR="$PWD/log" +EC2_INSTANCE=$(hostname) + +export MALLOC_ARENA_MAX=4 +# Stop the JVM from being allowed to use up all of +# Docker's virtual memory. Use if it's a problem +# see https://siddhesh.in/posts/malloc-per-thread-arenas-in-glibc.html + +set -eu + +trap 'error_handler' ERR 1 2 3 4 5 6 + +error_handler() { + ERROR_CODE=$? + echo "App crashed: $ERROR_CODE" + exit $ERROR_CODE +} + +cd $PWD + +# JMX prometheus exporter javaagent configuration +JMX_OPTS="-javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent-0.16.1.jar=8080:/opt/jmx_exporter/config.yaml" + +# APM Elastic javaagent configuration +APM_OPTS="-javaagent:/opt/elastic-apm/elastic-apm-agent-1.34.1.jar \ + -Delastic.apm.service_name=${APP_NAME} \ + -Delastic.apm.server_urls=${APM_HOST} \ + -Delastic.apm.secret_token=${APM_SECRET_TOKEN} \ + -Delastic.apm.environment=${ENVIRONMENT} \ + -Delastic.apm.config_file=/opt/elastic-apm/elastic.properties" + +# JVM options +JAVA_OPTS="-XX:InitialRAMPercentage=30 -XX:MaxRAMPercentage=85 -XX:+UseContainerSupport -XshowSettings:system " + +java $APM_OPTS \ + $JMX_OPTS \ + $JAVA_OPTS \ + -jar $JAR_PATH "$@" diff --git a/otus-30/jmx-config.yaml b/otus-30/jmx-config.yaml new file mode 100644 index 0000000..efe0a46 --- /dev/null +++ b/otus-30/jmx-config.yaml @@ -0,0 +1,2 @@ +rules: + - pattern: ".*" diff --git a/otus-30/logfile.log b/otus-30/logfile.log new file mode 100644 index 0000000..54f660d --- /dev/null +++ b/otus-30/logfile.log @@ -0,0 +1,3 @@ +18:57:29.592 [nREPL-session-b875359f-f3ce-40e2-a7a2-262c387ec591] INFO otus-30.core - Hello world +19:01:07.245 [nREPL-session-b875359f-f3ce-40e2-a7a2-262c387ec591] WARN otus-30.core - Hello world +19:01:15.257 [nREPL-session-b875359f-f3ce-40e2-a7a2-262c387ec591] ERROR otus-30.core - Hello world diff --git a/otus-30/project.clj b/otus-30/project.clj new file mode 100644 index 0000000..8b94af0 --- /dev/null +++ b/otus-30/project.clj @@ -0,0 +1,23 @@ +(defproject otus-30 "0.1.0-SNAPSHOT" + + :dependencies [[org.clojure/clojure "1.11.1"] + + [org.slf4j/slf4j-api "1.7.32"] + [ch.qos.logback/logback-classic "1.2.6"] + [org.clojure/tools.logging "1.2.4"] + + [com.brunobonacci/mulog "0.9.0"] + + [nrepl "0.9.0"] + + [com.amazonaws/aws-lambda-java-runtime-interface-client "2.4.0"]] + + + + :uberjar-name "production-app.jar" + + :main ^:skip-aot otus-30.core + + :repl-options {:init-ns otus-30.core} + + :profiles {:uberjar {:aot :all}}) diff --git a/otus-30/resources/logback.xml b/otus-30/resources/logback.xml new file mode 100644 index 0000000..e881a2e --- /dev/null +++ b/otus-30/resources/logback.xml @@ -0,0 +1,24 @@ + + + + + %d{HH:mm:ss.SSS} YOHOHO %-5level %logger{36} - %msg%n + + + + + + logfile.log + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/otus-30/src/otus_30/core.clj b/otus-30/src/otus_30/core.clj new file mode 100644 index 0000000..d2494d2 --- /dev/null +++ b/otus-30/src/otus_30/core.clj @@ -0,0 +1,39 @@ +(ns otus-30.core + (:gen-class) + (:require + [com.brunobonacci.mulog :as mulog] + [nrepl.server :as nrepl])) + + +;; package otus_30; +;; public class core { +;; public static void main(String[] var0) { +;; }} + +#_(defn -main [& args] + (println "Hello world")) + + + +(defn start-repl-server [port] + (nrepl/start-server + :port port + :bind "0.0.0.0")) + + +(defn -main [& args] + (start-repl-server 9999) + + (let [stop-mulog (mulog/start-publisher! + {:type :console + :pretty? true})] + + (mulog/log ::app-started + :timestamp (System/currentTimeMillis) + :message "App started" + :level :info + :data {:args "args"}) + + (println "Hello world") + + (stop-mulog))) diff --git a/otus-30/src/otus_30/lambda.clj b/otus-30/src/otus_30/lambda.clj new file mode 100644 index 0000000..f3434d1 --- /dev/null +++ b/otus-30/src/otus_30/lambda.clj @@ -0,0 +1,9 @@ +(ns otus-30.lambda + (:gen-class + :implements [com.amazonaws.services.lambda.runtime.RequestStreamHandler])) + + +(defn -handleRequest + "Implementation for RequestStreamHandler that handles a Lambda Function request" + [_ input-stream output-stream context] + (spit output-stream "Hello, World!")) diff --git a/otus-30/test/otus_30/core_test.clj b/otus-30/test/otus_30/core_test.clj new file mode 100644 index 0000000..c5bea94 --- /dev/null +++ b/otus-30/test/otus_30/core_test.clj @@ -0,0 +1,7 @@ +(ns otus-30.core-test + (:require [clojure.test :refer :all] + [otus-30.core :refer :all])) + +(deftest a-test + (testing "FIXME, I fail." + (is (= 0 1))))