1. ๊ธฐ๋ณธ ์ธํ
1.1 Admin Server
- maven ํน์ gradle์ spring-boot-admin-starter-server ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
<!-- https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-starter-server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.6.2</version>
</dependency>
// https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-starter-server
implementation 'de.codecentric:spring-boot-admin-starter-server:2.6.2'
- @EnableAdminServer ์ถ๊ฐ
@SpringBootApplication
@EnableAdminServer
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
1.2 Client
- maven ํน์ gradle์ spring-boot-admin-starter-client ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
<!-- https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-starter-client -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.6.2</version>
</dependency>
// https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-starter-client
implementation 'de.codecentric:spring-boot-admin-starter-client:2.6.2'
- application.yml ํน์ application.properties์ ์ค์ ์ถ๊ฐ
spring:
boot:
admin:
client:
url: http://localhost:58080 #admin server url
instance:
name: Client Server #admin UI ์์ ๋ณด์ฌ์ง ์ด๋ฆ ์ค์
์ฃผ์์ฌํญ
WAR, ์ฆ ์ธ๋ถ Tomcat์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ 'spring.boot.admin.client.instance.service-base-url'๋ฅผ ์ถ๊ฐํด์ฃผ์ด์ผ ํ๋ค. ์ถ๊ฐํ์ง ์์ผ๋ฉด ์๋์ ๊ฐ์ ์๋ฌ ๋ฐ์..! 'spring.boot.admin.client.instance.service-url'๋ก ์ค์ ํด๋ ๋ฌด๋ฐฉํจ!
java.lang.IllegalStateException: couldn't determine local port. Please set spring.boot.admin.client.instance.service-base-url.
at de.codecentric.boot.admin.client.registration.DefaultApplicationFactory.getLocalServerPort(DefaultApplicationFactory.java:192)
at de.codecentric.boot.admin.client.registration.DefaultApplicationFactory.getServiceBaseUrl(DefaultApplicationFactory.java:104)
at de.codecentric.boot.admin.client.registration.ServletApplicationFactory.getServiceUrl(ServletApplicationFactory.java:63)
at de.codecentric.boot.admin.client.registration.ServletApplicationFactory.getManagementBaseUrl(ServletApplicationFactory.java:76)
at de.codecentric.boot.admin.client.registration.DefaultApplicationFactory.getHealthUrl(DefaultApplicationFactory.java:154)
at de.codecentric.boot.admin.client.registration.DefaultApplicationFactory.createApplication(DefaultApplicationFactory.java:80)
at de.codecentric.boot.admin.client.registration.DefaultApplicationRegistrator.register(DefaultApplicationRegistrator.java:56)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
์ฌ๊ธฐ๊น์ง ์ค์ ์ ๋ง์น๊ณ Admin Server์ Client Server๋ฅผ ๊ตฌ๋์ํค๊ณ Admin Server์ ์ ์ํ๋ฉด ์๋์ ๊ฐ์ ํ๋ฉด์ ๋ณผ ์ ์๋ค.
Spring Boot 2๋ถํฐ๋ ์ด๋ ๊ฒ ๊ธฐ๋ณธ์ธํ ๋ง ํ๊ฒ ๋๋ฉด health ๋ฐ info์ด์ธ์ endPoint๊ฐ ๋ ธ์ถ๋์ง ์๋๋ค๊ณ ํ๋ค.
2. endPoints ๋ ธ์ถ ์ํค๊ธฐ
2.1 Client
- application.yml ํน์ application.properties์ ์ค์ ์ถ๊ฐ
management:
endpoints:
web:
exposure:
include: "*" #๋
ธ์ถ์ํฌ endpoint, *๋ ์ ์ฒด ๋
ธ์ถ
endpoint:
health:
show-details: always
include์ ์์คํ ๋ฆฌ์นด(*)์ ์์ ํฐ๋ฐ์ดํ(")๋ฅผ ๊ผญ ๋ฃ์ด์ฃผ์ด์ผ ํ๋ค! application.properties๋ก๋ ์ค์ ์ ์ํด๋ดค๋๋ฐ ์ด๊ฑด ๊ทธ๋ฅ ์์คํ ๋ฆฌ์นด๋ง ์ ์ด๋ ๋๋๋ฏํ๋ค.
management.endpoint.health.show-details ์์ฑ์ ๊ธฐ๋ณธ๊ฐ์ never ์ด๋ฏ๋ก ํญ์ ์์ธ ๋ด์ญ์ ๋ณด๊ณ ์ถ์ผ๋ฉด always๋ก ์ค์ ํ๋ฉด ๋๋ค.
๋ชจ๋ endPoints๊ฐ ๋ ธ์ถ๋๋๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์ ๋ค์ admin server์ ์ ์ํด๋ณด๋ฉด ์๋์ ๊ฐ์ด ์๋ฒ์ ์์ธํ ์ ๋ณด๊ฐ ํ์๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
3. Admin Server์์ Client Server์ ๋ก๊ทธ ํ์ธํ๊ธฐ
3.1 Client
- application.yml ํน์ application.properties์ ์ค์ ์ถ๊ฐ
logging:
config: classpath:logback-spring.xml
file:
name: D:\logs\logback.log
client server์์ ์์ฑ๋๋ logํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์ง์ ํด์ฃผ๋ฉด ๋๋ค. pattern์ผ๋ก๋ ํ ์ ์๋ค๋๋ฐ ๋๋ ์ผ๋จ ์ด๋ ๊ฒ ์ธํ ํด์ ํ์ธํ๋ค.๊ทธ๋ผ admin server์ ์ผ์ชฝ ๋ชฉ๋ก์ '๋ก๊ทธ>๋ก๊ทธํ์ผ' ์ด๋ผ๋ ํญ์ด ์๊ธฐ๊ณ , client server์ ๋ก๊ทธ๋ฅผ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์๋ค.
4. Login ์ถ๊ฐ
Spring Boot Admin์ ํตํด์ ํ๋ก์ธ์ค์ ์ํ ์ ๋ณด๋ฅผ ์กฐํํ๊ณ , ์ค์ ์ ๋ณด๋ ๋ณ๊ฒฝํ ์ ์๋ค.
๋ฐ๋ผ์ ํ์ฉํ์ง ์์ ์ฌ์ฉ์๊ฐ ์ด๋ฌํ ํ์๋ฅผ ํ ์ ์๋๋ก Spring Boot Security๋ฅผ ์ ์ฉํ์ฌ ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๋๋ก ํ์๋ค.
4.1 Admin Server
- maven ํน์ gradle์ spring-boot-admin-starter-server ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.6.2</version>
</dependency>
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security
implementation 'org.springframework.boot:spring-boot-starter-security:2.6.2'
- application.yml ํน์ application.properties์ ์ค์ ์ถ๊ฐ
server.port=58080
spring.security.user.name=admin
spring.security.user.password=admin
- SecuritySecureConfig.java ์ถ๊ฐ
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AdminServerProperties adminServer;
public SecurityConfig(AdminServerProperties adminServer) {
this.adminServer = adminServer;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
http.authorizeRequests()
// ๋ชจ๋ ๋ก๊ทธ์ธ ํ์ด์ง์ ๋ํ ๊ถํ์ ๋ถ์ฌํ๋ค.
.antMatchers(this.adminServer.path("/assets/**")).permitAll()
.antMatchers(this.adminServer.path("/login")).permitAll()
// ๊ถํ์ด ๋ถ์ฌ๋์ง ์์ผ๋ฉด ์ธ์ฆ์ ์์ฒญ ํ๋ค.
.anyRequest().authenticated()
.and()
// ๋ก๊ทธ์ธ ๋ฐ ๋ก๊ทธ ์์์ ๊ตฌ์ฑํ๋ค.
.formLogin().loginPage(this.adminServer.path("/login")).successHandler(successHandler).and()
.logout().logoutUrl(this.adminServer.path("/logout")).and()
// Spring Boot Admin ํด๋ผ์ด์ธํธ๋ฅผ ๋ฑ๋กํ๊ธฐ ์ํด HTTP-Basic ์ง์์ ์ฌ์ฉํ๋ค.
.httpBasic().and()
.csrf()
// ์ฟ ํค๋ฅผ ์ฌ์ฉํ์ฌ CSRF ๋ณดํธ ๊ธฐ๋ฅ ๊ตฌํ
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers(
// CRSF๋ฅผ ๋นํ์ฑํํ๋ค.
this.adminServer.path("/instances"),
// actuator EndPoint ๋ํ CRSF ๋ณดํธ๋ฅผ ๋นํ์ฑํํ๋ค.
this.adminServer.path("/actuator/**")
);
}
}
4.2 Client
- application.yml ํน์ application.properties์ ์ค์ ์ถ๊ฐ
: 4.1(Admin Server์ ์ค์ ํ์ผ)์์ ์ค์ ํ username/password ์ ๋ณด์ ๊ฐ์์ผ ํ๋ค.
spring:
boot:
admin:
client:
url: http://localhost:58080 #admin server url
username: admin
password: admin
instance:
name: Client Server
์ฌ๊ธฐ๊น์ง ์ค์ ํ๊ณ admin server์ ์ ์ํ๋ฉด ๊ธฐ์กด๊ณผ ๋ค๋ฅด๊ฒ ๋ก๊ทธ์ธํ๋ฉด์ด ๋จผ์ ๋ณด์ผ ๊ฒ์ด๋ค. ์ค์ ํ ID/PW๋๋ก ์ ๋ ฅํ๋ฉด ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๋ค.
๋ก์ปฌ์์ ์ ๋๋ค๊ฐ ์๋ฒ์ ์ฌ๋ฆฌ๋ SSLHandshakeException ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ์๋ฌ ๋ด์ฉ์ SSL์ธ์ฆ์์ DNS name๊ณผ ์ค์ domain์ด ๋ค๋ฅด๋ค๋ ๊ฒ์ด๋ค. (์๋ฒ์ ์ฌ๋ฆฐ client server๋ ssl์ธ์ฆ์๊ฐ ์ ์ฉ๋ https)
org.springframework.web.reactive.function.client.WebClientRequestException
No subject alternative DNS name matching ora19c found.; nested exception is javax.net.ssl.SSLHandshakeException: No subject alternative DNS name matching ora19c found.d.
Admin Server UI์์ ์ค์ client ์๋ฒ์ ๋๋ฉ์ธ์ด ์๋ ip ํน์ ์ฅ๋น๋ช ์ผ๋ก ๋ณด์ฌ์ง๊ณ ์์๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์ Client Server ์ค์ ์ 'spring.boot.admin.client.instance.service-url'์ ์ถ๊ฐํด์ฃผ์ด์ผ ํ๋ค.
ex) spring.boot.admin.client.instance.service-url = https://<client-domain>:<port>/
๋๊ธ