SpringBoot Actuator

· โ˜• 3 min read · ๐Ÿ‘€... views

1. Spring Boot Actuator

  • ์ผ๋‹จ.. ์ƒ์†Œํ•œActuator ์ด๋ž€??

    An actuator is a manufacturing term that refers to a mechanical device for moving or controlling something. Actuators can generate a large amount of motion from a small change.

    ๋ฌผ๊ฑด์„ ์›€์ง์ด๊ฑฐ๋‚˜ ์ปจํŠธ๋กคํ•˜๊ธฐ ์œ„ํ•œ ์žฅ์น˜๋ฅผ ์˜๋ฏธํ•˜๋Š” ์ œ์กฐ ์šฉ์–ด๋กœ, ์ž‘์€ ๋ณ€ํ™”๋กœ ํฐ ๋ณ€ํ™”๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

  • ์•ฑ์„ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•˜๊ฑฐ๋‚˜ ๋ฉ”ํŠธ๋ฆญ์„ ์ˆ˜์ง‘ํ•˜๊ฑฐ๋‚˜ ํŠธ๋ž˜ํ”ฝ ํ˜น์€ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์ƒํƒœ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์„ ์‰ฝ๊ฒŒ ํ•˜๋Š” ๊ฒƒ

  • ์„ค๋ช…๋งŒ ๋ด์„  ์•Œ๋“ฏ๋ง๋“ฏํ•˜๊ฒŒ ๋ชจ๋ฅด๊ฒ ๊ณ , ์ •๋ฆฌ๋ฅผ ํ•˜๋Š” ์‹œ์ ์—๋„ ๋”ฑ ์ด๊ฑฐ๋‹ค! ํ•˜๊ณ  ๋– ์˜ค๋ฅด์งˆ ์•Š๋„ค์š”.

  • ๋ชจ๋“  ๋‚ด์šฉ์€ spring boot2.x ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. 1.x ํ•˜๊ณ ๋Š” ์•ฝ๊ฐ„ ๋‹ฌ๋ผ์š”

2. ์˜์กด์„ฑ ์ถ”๊ฐ€

  • maven ์ผ ๊ฒฝ์šฐ
1
2
3
4
5
6
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
  • Gradle ์ผ ๊ฒฝ์šฐ
1
2
3
dependencies {
    compile("org.springframework.boot:spring-boot-starter-actuator")
}

3. Endpoints

  • ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์Œ

  • spring boot 2.x ์—์„œ๋Š” /health, /info 2๊ฐœ๋งŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต

  • ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ”„๋กœํผํ‹ฐ์— ์ถ”๊ฐ€

    management.endpoints.web.exposure.include = *
    
  • ๊ธฐ๋ณธ์ ์ธ /health ๋ฅผ ํ•˜๋ฉด

    1
    2
    3
    
    {
      "status": "UP"
    }
    

    ๋งŒ ๋‚˜์˜ค๋ฉฐ healthIndicator ์„ ๊ตฌํ˜„ํ•˜์—ฌ ์ถ”๊ฐ€์ ์œผ๋กœ ์ปค์Šคํ…€๋„ ๊ฐ€๋Šฅ

  • ex)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    @Component
    public class MyHealthIndicator implements HealthIndicator {
      
        @Override
        public Health health() {
            int errorCode = check(); // perform some specific health check
            if (errorCode != 0) {
                return Health.down().withDetail("Error Code", errorCode).build();
            }
            return Health.up().build();
        }
      
    }
    

4. Metrics

  • Micrometer ์ง€์›, ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉ

  • MeterRegistry ์œ ํ˜•์˜ Bean ์ด ์ž๋™ ๊ตฌ์„ฑ

  • ์ด๋ฒˆ ๊ธ€์„ ์“ฐ๊ฒŒ ๋œ ์ด์œ 

  • /metrics ๋ฅผ ํ•˜๋ฉด

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    {
        "names": [
            "http.server.requests",
            "jdbc.connections.idle",
            "tomcat.sessions.rejected",
            "hikaricp.connections.max",
            "hikaricp.connections.min",
            "process.cpu.usage",
            "jvm.memory.max",
            "jvm.threads.states",
            //....
        ]
    }
    

    ์ด๋Ÿฐ ์‹์œผ๋กœ ๋œจ๊ณ , ์‹ค์ œ ๊ฐ’์„ ์–ป์œผ๋ ค๋ฉด ์›ํ•˜๋Š” ๋ฉ”ํŠธ๋ฆญ (/actuator/metrics/jvm.memory.max) ์œผ๋กœ ์ด๋™ํ•ด์•ผ ๋จ

  • /actuator/metrics/jvm.memory.max ์˜ ๊ฒฝ์šฐ

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    {
        "name": "jvm.memory.max",
        "description": "The maximum amount of memory in bytes that can be used for memory management",
        "baseUnit": "bytes",
        "measurements": [
            {
            "statistic": "VALUE",
            "value": 9860284415
            }
        ],
        "availableTags": [
            {
            "tag": "area",
            "values": [
              "heap"
                //...
                ]
            }
        ]
    }
    
  • ๋‹น์—ฐํ•˜๊ฒŒ๋„ ์ปค์Šคํ…€ ๋ฉ”ํŠธ๋ฆญ ๋˜ํ•œ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    @Component
    public class SampleBean {
      
        private final Counter counter;
        private Tag tag = Tag.of("status", "ok")
      
        public SampleBean(MeterRegistry registry) {
            this.counter = registry.counter("received.message", "status", "all");
        }
      
        public void handleMessage(String message) {
            this.counter.increment();
            // handle message implementation
        }
    }
    
  • metrics ์— ๋“ฑ๋ก๋œ ๊ฒƒ ํ™•์ธ

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    {
        "names": [
            "http.server.requests",
            "jdbc.connections.idle",
            "tomcat.sessions.rejected",
            //....
            "received.message"
            ]
    }
    
  • /actuator/metrics/received.message ์—์„œ ๋‚ด์šฉ์„ ํ™•์ธํ•ด๋ณด๋ฉด

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    {
        "name": "received.message",
        "description": null,
        "baseUnit": null,
        "measurements": [
            {
            "statistic": "COUNT",
            "value": 1  // ํ˜ธ์ถœ ํšŸ์ˆ˜
            }
        ],
        "availableTags": [
            {
            "tag": "server_name",
            "values": [
                "sample-api"
                ]
            },
            {
            "tag": "profile",
            "values": [
                "local"
                ]
            },
            {
            "tag": "status",
            "values": [
                "all"
                ]
            }
        ]
    }
    

    ์ด๋Ÿฐ ์‹์œผ๋กœ ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Œ

5. Custom Endpoint ์ƒ์„ฑ

  • ์ปค์Šคํ…€ ์•ค๋“œ ํฌ์ธํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    @Component
    @Endpoint(id = "features")
    public class FeaturesEndpoint {
       
        private Map<String, Feature> features = new ConcurrentHashMap<>();
       
        @ReadOperation
        public Map<String, Feature> features() {
            return features;
        }
       
        @ReadOperation
        public Feature feature(@Selector String name) {
            return features.get(name);
        }
       
        @WriteOperation
        public void configureFeature(@Selector String name, Feature feature) {
            features.put(name, feature);
        }
       
        @DeleteOperation
        public void deleteFeature(@Selector String name) {
            features.remove(name);
        }
       
        public static class Feature {
            private Boolean enabled;
       
            // [...] getters and setters 
        }
    }
    
  • ์œ„์˜ ๊ฒฝ์šฐ๋Š” /actuator/features ๋กœ ์ ‘์† ๊ฐ€๋Šฅ

    • @ReadOperation โ€“ HTTP GET์— ๋งคํ•‘
    • @WriteOperation โ€“ HTTP POST์— ๋งคํ•‘
    • @DeleteOperation โ€“ HTTP DELETE์— ๋งคํ•‘
  • ์ด ์™ธ์—๋„ actuator ์— ๋Œ€ํ•œ ๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ™•์žฅํ•œ๋‹ค๊ฑฐ๋‚˜ logger ์— ๊ด€๋ จ๋œ ๊ฒƒ์ด๋ผ๋˜๊ฐ€, ๋ณด์•ˆ๊ณผ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ์ด๋ผ๋˜๊ฐ€…

  • ๋ฉ”ํŠธ๋ฆญ ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ ๋ง›๋ณด๊ธฐ๋กœ ์•Œ์•„๋ณธ ๊ฒƒ๋“ค์„ api ๋ฌธ์„œ ์ฐธ๊ณ ํ•˜๋ฉด์„œ ์ž‘์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋นˆ์•ฝํ•˜๊ฑฐ๋‚˜, ์ž˜๋ชป๋œ ๋‚ด์šฉ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

Share on

snack
WRITTEN BY
snack
Web Programmer


What's on this Page