Spring Cloud Bus
Actuator refresh 는 만약 변경 사항이 있을때 모든 MS 가 refresh 를 해야하는 번거로움이 있었다.
- Spring Cloud Bus 는 그러한 번거로움을 해결할 수 있다.
Spring Cloud Bus 사용
- 분산 시스템의 노드를 경량 메시지 브로커와 연결
- 여기서 노드는 Microservice(user-service...) 라고 생각하면 되고 우리가 사용할 메시지 브로커는 RabbitMQ 이다.
- 상태 및 구성에 대한 변경 사항을 연결된 노드에게 전달(Broadcast)
- 전달할때는 String 값 , 객체 , resource 등 다양한 것들을 전달할 수 있다.
정리하면 Spring Cloud Bus 가 Spring Cloud Config Server 에 직접 변경 사항을 push 한다고 생각하면 된다.
- HTTP POST /busrefresh 로 호출하면 된다 (bus-refresh EndPoint)
- endpoint 를 호출할 때는 어디서든 호출해도 된다.
- 즉 user-service , spring cloud config server , apigateway-service 등 아무곳에서 호출하면 된다.
- 아무곳에서 호출하기만 하면 Spring Cloud Bus 가 변경된 사항을 감지하고 자기와 연결된 다른 클라이언트(MS) 한테 업데이트를 하도록한다.
AMQP 와 Kafka
- AMQP (Advanced Message Queuing Protocol) : 메시지 지향 미들웨어를 위한 개방형 표준 응용 계층 프로토콜
- 메시지 지향 , 큐잉 , 라우팅 , 신뢰성 , 보안
- Erlang , RabbitMQ 에서 사용
- Kafka
- Apache Sotfware Foundation 이 Scalar 언어로 개발한 오픈 소스 메시지 브로커 프로젝트
- 분산형 스트리밍 플랫폼
- 대용량의 데이터를 처리 가능한 메시징 시스템
RabbitMQ vs Kafka
- RabbitMQ
- 메시지 브로커
- 초당 20개 이상 메시지를 소비자에게 전달
- 메시지 전달 보장 , 시스템 간 메시지 전달
- 브로커 , 소비자 중심
- Kafka
- 초당 100k+ 이상의 이벤트 처리
- Pub/Sub 로 나뉘어지고 Topic 에 메시지를 전달
- Ack 를 기다리지 않고 전달 가능
- 생산자 중심
RabbitMQ
- MacOS
- brew update
- brew install rabbitmq
- rabbitmq-server 로 실행
간단하게 실행할 수 있고 실행되면 15672 포트 번호로 들어가고 id, pw 모두 guest 로 하게 되면 들어갈 수 있다.
AMQP 사용해보기
Dependencies 추가
- Config Server : AMQP for Spring Cloud Bus + Actuator
- User Microservice , Gateway Service : AMQP for Spring Cloud Bus
application.yml 수정
- Config Server , user-service , apigateway-service : rabbitmq 의 host , port , username , password 정보 넣기 + actuator 에서 노출 시킬 속정에 busrefresh 추가하기
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
management:
endpoints:
web:
exposure:
include: refresh , health , beans , httptrace , busrefresh
- 위와 같이 각 service 에 rabbitmq 정보를 넣어주고 actuator 에서 busrefresh 도 추가해준다.
Spring Cloud Bus 테스트 해보기
- RabbitMQ 서버 기동
- discovery server 기동
- config-server 기동
- 각 MS 기동
spring:
cloud:
config:
uri: http://127.0.0.1:8888
name: config-service
- apigateway-service , user-service 모두 위와 같이 yml 파일을 설정한다.
- name 을 config-service 라고 하면 config-service 에 있는 application.yml 파일을 읽어온다.
- user-service 의 로그를 확인해보면 Located property source 부분에 어디서 설정 정보를 불러왔는지 나오는데 config-service 에서 지정한 경로에 있는 application.yml 파일을 가져온다.
POSTMAN 으로 회원가입을 하고 Token 을 가지고 health endpoint 를 호출해보자.
- 정상적으로 동작한다.
- 이때 application.yml 에서 token.secret 의 값을 변경했을 때 반영하는 것을 Spring Cloud Bus 를 이용해보자.
http://127.0.0.1:8000/user-service/actuator/busrefresh
POST user-service/actuator/busrefresh 로 호출하면 된다.
- 아무런 service 에서 호출하면 되고 호출하면 약간의 시간 뒤에 204 성공 코드가 나온다.
- 그런후 다시 로그인을 하고 해당 Token 을 가지고 확인해보면 변경된 값으로 토큰을 만든것을 확인할 수 있다.
- 해당 토큰으로 다른 service 를 실행 해봐도 정상적으로 동작한다.
- 즉 한 번의 refresh 로 모든 service 에 변경사항을 반영했다.
설정 정보의 암호화 처리
대칭키와 비대칭키
Encryption types
- Symmetric Encryption : 암호화 , 복호화 할 때 같은 키 값을 사용 (대칭)
- Asymmetric Encrpytion : 암호화 , 복호화 할 때 다른 키 값을 사용 (비대칭)
만약 데이터베이스 아이디 , 비밀번호는 중요한 정보이기 때문에 해당 정보들을 암호화해서 저장을 한다.
- {cipher} 가 붙어있으면 암호화된 상태이다.
- Spring Cloud COnfig Server 에서 암호화 된 것들을 바꿔서 service 에 보내는 것이다.
config-server 에서 bootstrap.yml 파일에 encrypt:key: 로 암호화할 키를 설정할 수 있다.
- 그런후에 POSTMAN 에서 POST 8888/encrypt 주소에 Body->Text 에 암호화할 단어를 적으면 응답값이 암호화된 문자열이 나온다.
- 반대로 해당 암호화된 문자열을 POST 8888/decrypt 로 요청하면 복호화되서 나온다.
Users Microservice 의 applicatino.yml , bootstrap.yml 파일을 수정
- config-server 의 user-service.yml 로 이동
- user-service 에 있는 datasource 에 해당하는 정보들을 config-server 에 옮기고 값을 넣을 때 암호화를 해서 넣는다.
- password 를 암호화 해서 넣을 것인데 이때 위에서 설명한 방식으로 암호화를 시키고 '{cipher}암호화된 문자열' 형식으로 넣어주면 된다.
- {cipher} 가 있으면 암호화가 된 문자열로 인식한다.
encrypt:
key: jiwonjiwonjiwonjiwonjiwon123
- POSTMAN 에서 8888/encrypt , 8888/decrypt 를 사용해서 암호화 , 복호화를 할 수 있다.
user-service 에서 application.yml 에 있는 datasource: 에 있는 값들을 config-server 에 옮긴다.
spring:
cloud:
config:
server:
git:
uri: file:///Users/supportkim/Desktop/git-local-repo
- user-service의 application.yml 에 있는 datasrouce: 에 값들을 위에 있는 uri 경로에 user-service.yml 를 만들고 여기에 값들을 넣는다.
spring:
cloud:
config:
uri: http://127.0.0.1:8888
name: user-service
- 그런후 user-service 의 bootstrap.yml 에다가 위에 처럼 설정하면 config-server 에 있는 user-service.yml 을 찾고 해당 값을 가지고 datasource 으로 사용할 것이다.
이제 datasource: 값중에서 password 를 암호화해서 넣어보자.
- 비밀번호를 1234 로 지정했다면 POSTMAN 으로 1234 의 암호화된 값을 가져오고 그 값을 넣어주면 된다.
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
username: sa
password: '{cipher}ad9ccf4ecb3e774ce964725a9012e235e4a22761c3d7cadab01f71b48eab15ef'
이렇게 설정하고 모든 서버를 기동한 후 8888/user-service/default 로 접근해보자.
- 우리는 yml 파일에 암호화된 pw 를 넣었지만 해당 URI 에 접근하면 1234 로 나오는 것을 확인할 수 있다.
- user-service 의 포트번호를 알아오고 포트번호/h2-console 로 접근하고 password 를 입력하지 않으면 오류가 발생한다.
- 이제 여기에 1234 를 넣으면 정상적으로 연결이 된다.
이렇게 암호화된 정보를 설정 파일에 넣고 해당 정보를 복호화해서 사용하는 방법을 알아봤다.
- Public , Private Key 생성 -> JDK keytool 이용
- keytool 명령어로 만들 수 있다.
- config-server의 bootstrap.yml 에서 encrypt:key-store: 부분을 넣어주면 비대칭키 암호화를 사용할 수 있다.
- 위에서 했던 것 처럼 POSTMAN 에서 8888/encrypt 로 요청한 값으로 yml 파일에 password 를 넣으면 된다.
해당 명령어로 jks 파일을 만들 수 있다.
keytool -genkeypair -alias apiEncryptionKey -keyalg RSA -dname "CN=jiwon,
OU=API Development,O=jiwonkim.co.kr, L=Seoul, C=KR" -keypass "test1234"
-keystore apiEncryptionKey.jks -storepass "test1234"
해당 명령어로 만든 jks 파일의 내용을 볼 수 있다.
keystore % keytool -list -keystore apiEncryptionKey.jks -v
인증서 파일 만드는 명령어
keytool -export -alias apiEncryptionKey -keystore apiEncryptionKey.jks -rfc -file trustServer.cer
인증서 파일로 jks 파일 만드는 명령어
keytool -import -alias trustServer -file trustServer.cer -keystore publicKey.jks
- 해당 예제에서는 첫 번째 명령어만 사용해서 jks 파일을 만들면 된다.
- 만들 때 파일을 하나 만들어서 그 파일에다가 만들면 된다.
encrypt:
key-store:
location: file://${user.home}/Desktop/keystore/apiEncryptionKey.jks
password: test1234
alias: apiEncryptionKey
- 비대칭키를 이용할 때는 yml 파일을 위와 같이 작성하면 되고 각 정보들은 명령어를 통해 jks 파일을 만들 때 설정한 값들을 넣어주면 된다.
- 대칭키에서도 했던 것 처럼 8888/encrypt 로 들어가서 암호화를 하고 그 암호화한 값을 user-service.yml 에서 password 에다가 넣어주면 된다.
- 넣을 때 {cipher} 를 앞에 붙어줘야한다.
여기서는 apigateway-service 의 bootstrap.yml 을 보면 ecommerce.yml 을 읽어오는 것으로 해놨다.
- ecommerce.yml 에 token 정보가 있고 그 정보를 가져와서 사용을 했었다.
- 여기서 ecommerce.yml 에 있는 token 정보를 비대칭키 암호화를 해서 넣고 그것을 복호화해서 사용하도록 할 수 있다.
- ecommerce.yml 에서 token.secret 부분을 8888/encrypt 로 암호화를 하고 그 부분을 {cipher}+암호화된 키 를 넣어주면 된다.
참고자료