사내 발표로 진행한 스프링부트 소개.


나는 STS로.... Gradle, YML

실시간 스트리밍하기 쉽네....

이번 주말에 시험방송 함 해볼까....

올해 1월에 만들어놓고... 요 근래에 들어서 속도를 올리는 중이다. 

이제 1.3.0 버전이 나올 예정인데... 번역은 더디기만 하다.

10월말까지 해서 번역을 완료하고, 1.3.0.BUILD-SNAPSHOT 보고 업글하여 연말에는 번역을 마치는 것을 목표로 하고 있다.

구글번역기, 다음사전 을 애용중...

스프링부트가 가지고 있는 강력한 기능 중 하나가, 바로 액츄에이터 이다. 그 중에서 내가 지금까지 몰랐던 Remote Shell에 대한 기능을 살펴본다. 리모트쉘은 별다른 코딩을 하지 않아도 사용이 가능하다.

리모트쉘의 의존성은 다음과 같다.

dependencies {
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-remote-shell")
    // 생략
}

org.springframework.boot:spring-boot-starter-security 을 추가하지 않으면 다음 클래과 같은 예외가 발생하며 실패한다.

Caused by: java.lang.ClassNotFoundException: org.springframework.security.config.http.SessionCreationPolicy
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 42 more
:bootRun FAILED

SSH와 관련해서 필요한 것으로 보인다. 리모트쉘의 기본포트는 2000번이다. 이 부분은 다음에 있는 설정코드에서 수정가능하다. 리모트쉘에서 사용하는 속성은 다음과 같다. 자세한 내용은 레퍼런스 문서를 살펴보기 바란다.

# REMOTE SHELL
shell.auth=simple # jaas, key, simple, spring
shell.command-refresh-interval=-1
shell.command-path-patterns= # classpath*:/commands/**, classpath*:/crash/commands/**
shell.config-path-patterns= # classpath*:/crash/*
shell.disabled-commands=jpa*,jdbc*,jndi* # comma-separated list of commands to disable
shell.disabled-plugins=false # don't expose plugins
shell.ssh.enabled= # ssh settings ...
shell.ssh.key-path=
shell.ssh.port=
shell.telnet.enabled= # telnet settings ...
shell.telnet.port=
shell.auth.jaas.domain= # authentication settings ...
shell.auth.key.path=
shell.auth.simple.user.name=
shell.auth.simple.user.password=
shell.auth.spring.roles=

내가 테스트를 위해 설정한 속성은 다음과 같다. application.yml:

# @author: honeymon
 
management:
  security:
    enabled: true
shell:
  telnet: # Tel
    enabled: true
    port: 10000
  ssh:
    enabled: true
    port: 10001
  auth:
    simple:
      user:
        name: honeymon
        password: remote-shell

리모트쉘에 접근하는 방법은 다음과 같다.

ssh -p <port-number> <shell.auth.simple.user.name 값>@접근서버 IP 혹은 호스트명
//위의 설정에 따르면 다음과 같이 접근이 가능하다.
ex) $ ssh -p 10001 honeymon@localhost

텔넷으로 접근하려면 다음의 의존성을 추가해야 한다.

If you want to also enable telnet access you will additionally need a dependency onorg.crsh:crsh.shell.telnet

search.maven.org crsh.shell.telnet 검색 telnet 접속해보려고 했더니 오류가 발생...

Caused by: java.lang.NoSuchMethodError: org.crsh.vfs.Resource.<init>(Ljava/net/URL;)V
    at org.crsh.telnet.TelnetPlugin.init(TelnetPlugin.java:61)
    at org.crsh.plugin.PluginManager.getPlugins(PluginManager.java:83)
    at org.crsh.plugin.PluginContext.start(PluginContext.java:327)
    at org.crsh.plugin.PluginLifeCycle.start(PluginLifeCycle.java:104)
    at org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration$CrshBootstrapBean.init(CrshAutoConfiguration.java:230)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:349)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:300)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)
    ... 17 more

처음 접근할 경우에는 다음과 같이 인증키를 저장할지를 묻는다. 

리모트쉘로 접근하면 다음과 같은 화면을 볼 수 있다. 

리모트쉘에서 사용할 수 있는 명령어는 help 를 타이핑하면 다음과 같은 내용을 볼 수 있다. 

> help
Try one of these commands with the -h or --help switch:
 
NAME       DESCRIPTION
autoconfig Display auto configuration report from ApplicationContext
beans      Display beans in ApplicationContext
cron       manages the cron plugin
dashboard  a monitoring dashboard
egrep      search file(s) for lines that match a pattern
endpoint   Invoke actuator endpoints //앱에 설정된 애드포인트 목록들을 확인가능하다.
env        display the term env
filter     a filter for a stream of map
java       various java language commands
jmx        Java Management Extensions
jul        java.util.logging commands
jvm        JVM informations
less       opposite of more
mail       interact with emails
man        format and display the on-line manual pages
metrics    Display metrics provided by Spring Boot
shell      shell related command
sleep      sleep for some time
sort       sort a map
system     vm system properties commands
thread     JVM thread commands
help       provides basic help
repl       list the repl or change the current repl

리모트쉘은 CRaSH를 사용했다. 배너를 바꾸고 싶은데... 그 중에서 우와!!하면서 감탄했던 것이 대시보드!

화면크기에 따라 리사이징도 된다! +_+)b

스프링 액츄에이터SpringBoot actuator 에서 제공하는 엔드포인트 중 하나인 metrics 정보를 모니터링할 수 있는 metrics


리모트쉘로 원격접속해서 애플리케이션의 상태를 살필 수 있는 수단이 생겼다는 건 애플리케이션을 관리하는데 더욱 편해진다는 것이다. 리모트쉘로 접근해서... endpoint invoke shutdown을 실행하면!!

당신은 주금!!(애플리케이션을 켜로 터미널로 서버에 접속해야하는 번거로움이 생길 것이다).

리모트쉘에서 애플리케이션을 끄기 위해서는

리모트쉘에서 셧타운을 실행하기 위해서는 application.properties(or yml)에 다음 항목을 추가해야한다.

endpoints.shutdown.enabled=true

이렇게 가볍게 스프링부트가 제공하는 기능 중 하나인 리모트쉘Remote shell을 살펴봤다. 리모트쉘을 통해서 애플리케이션의 상태를 모니터링하고 제어하는 것이 가능해졌다. 스프링부트를 기반으로 배포하는 앱에는 반드시 넣어줘야할 녀석이었다. 바로! 추가!


스프링부트만 믿고 개발에 정진해왔다. 그러다가 시연해야할 떄가 되어 젠킨스를 통해서 개발서버에 빌드-배포된 war 파일을 실행하여 테스트하려고 하니 Thymeleaf template engine에서 template file 을 찾지못하는 문제가 발생했다.

이 문제가 왜 생기는지를 고민하고 검색해봐도 별다른 내용을 찾을 수가 없었다.

스프링부트 튜토리얼을 살펴보다가, 컨트롤러 영역에서 조금 다른 차이점을 찾아냈다.

@Controller
public class TestController {

    @RequestMapping("/test")
    public String test() {
        return "test";
    }
}

문제가 발생하는 화면의 컨트롤러에서는

@Controller
public class TestController {

    @RequestMapping("/test")
    public String test() {
        return "/test";
    }
}

와 같은 형태로 정의가 되어 있는 것이다.

차이를 발견했는가???

저 문제를 찾지 못해서 헤매였다. 1시간여를…. 흙… Orz…

해결방법

return "/test";return "test"; 으로 정의하면 된다.
bootRepackage로 빌드된 애플리케이션 배포본은 경로에 민감하게 반응하는 것으로 보인다.

저 ‘/‘ 때문에 ‘/test’ 에 대한 접근이 아니라 ‘//test’로 처리하기 때문에 나타나는 문제가 아닐까 추측해본다.

+ Recent posts