Phase 1
3. 스프링 부트로 마이크로서비스 구축하기
4. 도커
https://hyune-c.tistory.com/53
Phase 2
5. 스프링 클라우드 컨피그 서버로 구성 관리
6. 서비스 디스커버리
이 글입니다.
eureka-server 구현
유레카는 클라이언트의 생존 여부를 체크하여 API 게이트웨이의 라우팅을 돕습니다.
eureka-server
// build.gradle.kts
dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-server")
}
// EurekaServerApplication.kt
@EnableEurekaServer
@SpringBootApplication
class EurekaServerApplication
fun main(args: Array<String>) {
runApplication<EurekaServerApplication>(*args)
}
// application.yml
server:
port: 8761
eureka:
instance:
appname: eureka-server
client:
register-with-eureka: false
fetch-registry: false
licensing-service
// build.gradle.kts
dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:3.1.4")
}
// LicensingServiceApplication.kt
@EnableEurekaClient
@SpringBootApplication
class LicensingServiceApplication
fun main(args: Array<String>) {
runApplication<LicensingServiceApplication>(*args)
}
// application.yml
server:
port: 8080
eureka:
instance:
appname: licensing-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/
http://localhost:8761/
유레카는 클라이언트를 알지 못합니다.
클라이언트가 유레카의 url 을 알고 정보를 보내줍니다. (application.yml 의 defaultZone)
유레카는 클라이언트 application 의 생존 여부를 알 수 있습니다. (LICENSING-SERVICE)
http://localhost:8761/eureka/apps
유레카에서 관리되는 application 을 볼 수 있습니다. json 형태도 가능합니다.
instanceId 의 ip 를 보면 172 대역대로 사설망임을 확인할 수 있습니다.
API 호출
ip 로 api 요청 시 성공합니다!
docker setting
docker 는 시행 착오가 조금 있었습니다. 코드 소개 후 아래에서 설명 합니다.
Dockerfile
# docker/eureka-server/Dockerfile
FROM openjdk:17-jdk-slim-buster
ARG JAR_FILE=/eureka-server/build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# docker/eureka-server/licensing-service
FROM openjdk:17-jdk-slim-buster
ARG JAR_FILE=/licensing-service/build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
기존 root 경로에 있던 Dockerfile 을 service 에 맞는 폴더로 분리했습니다.
docker setting 후 이어질 환경 설정 분리에서 좀 더 활용해봅니다.
docker-compose.yml
version: "3"
services:
eureka-server:
image: eureka-server
ports:
- "8761:8761"
licensing-service:
image: licensing-service
ports:
- "8080:8080"
links:
- eureka-server
run.sh
# eureka-server
cd eureka-server
./gradlew bootjar
cd ..
docker build -f docker/eureka-server/Dockerfile -t eureka-server .
# licensing-service
cd licensing-service
./gradlew bootjar
cd ..
docker build -f docker/licensing-service/Dockerfile -t licensing-service .
# 백그라운드로 실행
nohup docker-compose up 1>/dev/null 2>&1 &
기존에는 서비스가 1개였기에 바로 실행했습니다.
하지만 이제는 서비스가 2개인만큼 백그라운드로 실행하게 하고 로그를 남기지 않게 했습니다.
./run.sh
docker dashboard 에서 8761, 8080 포트로 정상실행 된 것을 볼 수 있습니다.
(개별 서비스로 실행했을 때와 달리 docker 로 실행 시 30초 정도의 지연이 걸릴 수 있습니다.)
정상적으로 클라이언트를 찾은 것 처럼 보이지만 IP 가 보였던 아까와는 달리 해시 값이 보입니다?
API 호출도 되지 않습니다???
시행 착오 1 - IP 가 왜 안 나올까?
docker dashboard 를 보면 licensing-service-1 의 컨테이너 ID 와 유레카의 해시 값이 같음을 알 수 있습니다.
하지만 IP 의 형태가 아니고 API 호출이 정상적으로 되지 않습니다.
처음에는 설정의 문제로 생각해서 eureka.instance.preferIpAddress=true 도 설정했지만 결과는 같았습니다.
그러다 책을 다시 보니 흐름상 클라이언트의 요청은 API 게이트웨이를 통해 서비스로 전달되고 있었습니다.
그리고 8장에서 서비스 라우팅에 대해 나오는 것을 볼 때 굳이 지금 더 찾아봐야 될 이유가 없을 것 같아 보류했습니다.
시행 착오 2 - 도커 컨테이너에서 localhost 를 쓰면 어떻게 될까?
기존 설정은 그대로 둔 채 docker 세팅만 완료한 후 기동 했을 때, 기동은 되었지만 유레카에서 보이지 않았습니다?
이상해서 로그를 보니 유레카의 url 을 찾지 못하고 있었습니다!
관련 자료를 찾고 다시 생각해보니 당연한 것이었습니다.
docker 는 컨테이너 단위로 동작함으로 port 만으로는 접근이 불가능합니다!
찾아보니 host 의 ip 에 접근할 수 있는 방법이 있어 설정을 바꾸니 정상 접근되었습니다.
# licensing-service
# application.yml
eureka:
instance:
appname: licensing-service
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
# defaultZone: http://localhost:8761/eureka/ # localhost 기동시
defaultZone: http://host.docker.internal:8761/eureka/ # docker 기동시
환경 변수 관리 by Dockerfile
책에서는 Spring Cloud Config 의 장점을 말합니다.
하지만 저는 강한 필요성을 느끼지 못했기에 Dockerfile 로 접근하였습니다.
서비스와 config 를 분리할 수 있다.
# application.yml
server:
port: ${SERVER_PORT}
# Dockerfile
FROM openjdk:17-jdk-slim-buster
ENV SERVER_PORT=8080 # 환경 변수 주입
ARG JAR_FILE=/licensing-service/build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
제가 가장 좋아하는 방식으로 Dockerfile 을 활용하여 환경 변수를 관리합니다.
dev, stg, prod 별로 Dockerfile 을 관리할 수도 있고, 작은 프로젝트에서는 서비스 안에 둘 수도 있습니다.
평소에는 설정 정보만 모아두는 git repository 를 운영합니다.
그리고 배포 스크립트 작성 시 (git action) 서비스에 해당하는 Dockerfile 을 가져와 실행할 수 있습니다.
개인적으로 사이드 프로젝트할 때는 배포에 elastic beanstalk 를 활용합니다.
elastic beanstalk 을 활용하면 AWS 에서 안전하게 환경 변수를 관리할 수 있어 참 편했습니다.
사용해보지는 않았지만 AWS Systems Manager, Secrets Manager 역시 관련된 솔루션으로 알고 있습니다.
서비스 인터페이스로의 접근 방식을 추상화할 수 있다.
# application.yml
spring:
datasource:
url: ${JDBC_URL:jdbc:h2:mem:testdb}
driverClassName: org.h2.Driver
username: ${JDBC_USERNAME:sa}
password: ${JDBC_PASSWORD:}
이것 역시 Dockerfile 을 통해 관리할 수 있습니다.
기본 값을 활용하면 local 기동시 편하고 안전해 유용하게 사용하고 있습니다.
config 를 중앙 관리할 수 있다.
별도의 git repository 에 Dockerfile 을 관리하는 방법으로 대체할 수 있습니다.
이력 관리도 돼서 유용합니다.
config 를 변경 감지할 수 있다.
이것은 불가능합니다.
하지만 개인적인 경험으로 볼 때 config 가 바뀌면 코드의 변경은 필연적이었습니다.
(네트워크 설정이 바뀔 때는 코드의 변경이 없어도 되지만, 그런 경우는 AWS 의 영역이었습니다.)
그렇기 때문에 변경 감지가 저에게는 그다지 유용한 기능으로 와닿지 않았습니다.
마치며
이번 Phase 를 진행하면서 전 회사 생각이 많이 났습니다.
전 회사에서는 docker 를 적극적으로 쓰긴 했지만, 대부분 팀장님이 세팅한 것을 받아쓰기만 했었습니다.
하지만 이번에 docker 를 세팅해보면서 이렇게 좋고 편한 걸 왜 그동안 안쓴거지? 라는 생각이 많이 들었습니다.
이미 현대의 배포는 모두 수평 확장을 기반하고 있고 컨테이너 기반으로 움직이기에 유용할 것 같습니다.
좀 더 잘 쓸 수 있도록 공부해야겠습니다!
config 에 대해서도 그렇습니다.
Dockerfile 로 관리되긴 했지만 서비스와 같은 repository 에 있었기에 보안 지적을 받았기에, s3 로 이관하고 배포 스크립트에서 가져와 활용하는 것을 지원했었습니다.
그때도 말한 내용이지만 s3 로 이관되면 이력 관리도 힘들어지고, 어차피 private repository 가 털린 상황이면 s3 도 위험하긴 마찬가지다. 라고 생각합니다. 별도의 git repository 로 이관하는 것 까지도 말했지만.. 결국은 s3 로 결정된 슬픈 기억이 떠올랐습니다. 보안은 참 어렵네요.
spring cloud config 가 좋은 기술인 건 분명하지만, 아직 저에게는 와닿지 않고 있습니다.
복잡도가 높아져 꼭 도입해야만 하는 시점이 오면 좋겠다는 생각이 들었습니다.
'Series > 스프링 마이크로서비스 코딩 공작소' 카테고리의 다른 글
스프링 마이크로서비스 코딩 공작소 (1) (2) | 2022.10.02 |
---|