11장 프로덕션 레디 서비스 개발
목차
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
11장 프로덕션 레디 서비스 개발
11.1 보안 서비스 개발
__11.1.1 기존 모놀리식 애플리케이션의 보안
__11.1.2 마이크로서비스 아키텍처에서의 보안 구현
11.2 구성 가능한 서비스 설계
__11.2.1 푸시 기반의 외부화 구성
__11.2.2 풀 기반의 외부화 구성
11.3 관측 가능한 서비스 설계
__11.3.1 헬스 체크 API 패턴
__11.3.2 로그 수집 패턴
__11.3.3 분산 추적 패턴
__11.3.4 애플리케이션 지표 패턴
__11.3.5 예외 추적 패턴
__11.3.6 감사 로깅 패턴
11.4 서비스 개발: 마이크로서비스 섀시 패턴
__11.4.1 마이크로서비스 섀시
__11.4.2 이제는 서비스 메시로
11.5 마치며
정리 전략?
- 전부 정리는 NO
- 핵심중에서 꼭 적고 가야겠다 싶은 것만!
- 나의 생각을 섞어서 적을것..!
- 떠오르는 것 먼저 간단하게..!
내용
11.1 보안 서비스 개발
- 보안서비스를 개발할때 요새는 Oauth2.0의 프로토콜로 개발하여 서비스하는 편임.
- 애플리케이션 개발자는 주로 다음 네가지의 보안 요소를 구현한다.
- 인증 : 애플리케이션에 접근하는 애플리케이션이나 사람의 신원을 체크한다. 일반적으로 인증키나 로그인 계정과 비밀번호를 사용한다.
- 인가 : 어떤 주체가 요청을 보내왔을 경우 해당 요청을 수행할 수 있는 권한이 있는지 체크 한다.일반적으로 역할기반 (ROLE) 이나 접근제어리스트(ACL Access Control List) 를 함께 사용한다.
- 감사 : 보안이슈탐지, 컴플라이언스 시행 등, 고객지원을 위해 주체가 수행하는 작업을 추적한다.
- 보안 IPC : 드나드는 모든 요청은 TLS 를 경유 하도록 한다. 출처: https://freeend.tistory.com/116 [FreeEnd:티스토리]
__11.1.1 기존 모놀리식 애플리케이션의 보안
- 세션 기반으로 로그인 상태를 유지, 관리하고, 인증, 인가 관리를 했었음.
- 기존 모놀리식 애플리케이션에서 사용자의 인증은 ID/패스워드 방식으로 진행한다. 사용자(클라이언트)가 ID/패스워드를 애플리케이션에 POST 로 요청한다. 애플리케이션은 요청온 자격증명이 맞으면 클라이언트에게 세션토큰을 반환한다. 세션 토큰을 받은 클라이언트는, 다음 요청서부터 세션 토큰을 포함시켜 서버로 전송시킨다. 애플리케이션은 이 HttpSession 객체를 메모리에 보관 해두고 있다가, 클라이언트의 요청에 포함되어있는 session tocken 를 식별해 인증한다. 보안 컨텍스트(Security Context) 는 인증된 정보를 저장하는 것을 말한다. 애플리케이션은 현재 요청을 보내온 사용자 정보 인증 정보를 ThreadLocal 을 이용해 정적 영역에 저장한다. 이러면 요청 내에서 언제든지 사용자의 정보를 불러내 확인할 수 있다.
__11.1.2 마이크로서비스 아키텍처에서의 보안 구현
- msa 방식에서 세션 방식의 인증 인가는 별로라는 내용을 이야기한듯.
- 별로인 이유? : 세션 값이 서로다른 서비스에서도 인식 및 확인 가능할 정도여야 하면, 별도의 저장 관리가 필요함. 글로벌 저장소.
- 그들 사이의 접속을 관리하는 것도 이슈임.
- 구성 가능한 서비스를 설계하려면 oauth2.0에 나오는 그것을 참고하여 구현을 해보자.
- 인증 서버
- 리소스 서버
- 사용자
- 하나는 뭐더라?
어려운 이유..
- 인-메모리 보안 컨텍스트 (in-memory security context) : Threadlocal 등을 인-메모리 보안컨텍스트를 이용해 사용자의 신원을 전달하는 방법이 있으나, 마이크로 서비스에서는 메모리가 공유되지 않으므로 사용할 수 없다. 중앙화 세션 (centralized session) : 인-메모리 세션도 보안컨텍스트와 마찬가지로 사용할 수 없다. 우회적으로 마이크로서비스의 느슨한 결합 원칙에 위배되지만, DB에 세션정보를 넣고 각 서비스가 동일하게 접근할 수도 있다.
API 게이트웨이에서 인증 처리
인증을 처리하는 방법은 여러가지나, 개별 서비스에서 인증을 구현할 경우 다음과 같은 문제가 생긴다. 모든 개발자가 제대로 보안을 개발 하리라는 보장이 없기때문에 보안적 취약점이 발생 될 수 있다. 이로인해 미인증 요청이 발생할 수 있다. 또, 클라이언트가 매번 자격증명을 전송한다던지, 애플리케이션이 매번 신규토큰을 생성 할 수 도 있다. 때문에 가장 이상적인 방법은 API 게이트웨이를 이용하는 방식이다. 인증에 문제가 생기면 API 게이트웨이만 바로잡으면 되고, 개별 서비스에서 구현하는 로직을 게이트웨이에만 작성하면 된다. API 게이트웨이로부터 호출받은 서비스는 게이트웨이로부터 받은 토큰으로 요청을 한번더 검증한다.
인가처리 -> 서비스에서 하자!
가작업은 클라이언트가 요청한 작업이 권한이 있는지 검사하는 작업이다. 이 작업역시 API 게이트웨이에서 구현 가능하다. 권한이 없는 요청 인입시 라우팅 전에 거부하면 그만이다. 하지만 API 게이트웨이에 인가로직을 두면 URL 등을 게이트웨이가 직접 챙겨야 하므로 게이트웨이와 서비스가 강한 결합이 발생 하게 된다. 따라서 인가로직은 서비스에 구현하는 편이 낫다. 서비스가 역할기반으로 URL 과 METHOD를 인가하고, ACL로 애그리거트 접근을 따로 관리한다.
JWT로 사용자 신원/역할 전달
난독화 토큰 (opaque token) : 일반적으로 UUID (Universally Unique Identifier) 를 많이 사용한다. 단, 성능 및 가용성이 떨어지고, 지연시간이 길다. 투명 토큰 (transparent token) : JWT (Json Web Token) 이 사실상 표준이다. 두 당사자간의 신원/역할등 정보를 안전하게 보관 가능하다. JWT 는 인증정보, 토큰의 만료일자 등이 담긴 JSON 객체를 payload 에 담아 JWT 생성자(게이트웨이) 와 수신자 (서비스) 만 알수있는 코드로 암호화한다. 위변조가 불가능해 보안성이 강력하다. 그러나, JWT 에 토큰이 포함되어 있으므로 취소가 불가능해 토큰이 유출 될 경우 문제가 발생한다. 때문에 유효기간을 가능한 짧게 생성해 유출된 토큰의 오용을 막는다. -> 뒤에 리프시토큰과 혼용하여 강화 가능.
OAuth 2.0 응용
OAuth 2.0 의 핵심 개념은 다음과 같다.
- 인증서버 (Authorization Server) : 사용자 인증 및 액세스/리프레시 토큰 획득 API 를 제공한다.
- 액세스 토큰 (Access Token ) : 서비스 접근을 허가하는 토큰이다.
- 리프레시 토큰 (Refresh Token) : 클라이언트가 새 액세스 토큰을 얻기 위해 필요한 토큰이다.
- 리소스 서버 (Resource Server) : 액세스 토큰으로 접근을 허가하는 서비스. 곧 마이크로서비스를 제공하는 서비스를 말한다.
- 클라이언트 : 리소스서버에 접근하려는 클라이언트. ** 마이크로 서비스 아키텍처 에서는 API 게이트 웨이가 이 클라이언트이다! **
- 액세스 토큰이 이미 거의 만료된경우, ** API 게이트웨이 ** 는 인증서버에 OAuth2.0 리프래시 승인을 요청해서 액세스 토큰을 새로 발급받습니다.
- OAuth2.0은 널리 검증된 보안 표준이다!
11.2 구성 가능한 서비스 설계
- 구성을 application.yaml과 같은 것으로 구성하는것은 보안 이슈가 있고, 그렇다고 빼고 따로 하자니 관리가 안됨.
- 구성을 외부에서 구성해서 가져오자!
- 스프링 프레임워크의 프로파일 장치로 런타임에 프로퍼티 세트를 선택하는 구조는 보안에 취약하고 배포에 한계가 있다. 게다가 자격증명처럼 민감한 데이터는 볼트와 같은 보안 저장 장치로 안전하게 저장되어야 한다. 따라서 외부화 구성 패턴에 따라 런타임에 적잡한 구성 프로퍼티를 서비스에 제공하는 방법을 권장한다.
__11.2.1 푸시 기반의 외부화 구성
- 서비스 인스턴스가 생성될 때 프로퍼티 값을 제공한다.도커 컨테이너를 통해 환경 변수를 지정하는 방법도 이 중 하나이다. 푸시 모델은 지금도 널리 사용되는 서비스 구성 메커니즘이지만 이미 실행 중인 서비스를 재구성하기는 어려운 한계가 있다. 구성 프로퍼티 값이 여러 서비스에 흝어지는 것도 문제이다.
- 이전에 참여했던, zipple 프로젝트가 이런식으로 되어있었다!
- aws.region 프로퍼티 값은 구성 파일, aws_REGION 환경 변수 등 여러 소스 중 하나에서 읽습니다.
- 이전에 참여했던, zipple 프로젝트가 이런식으로 되어있었다!
__11.2.2 풀 기반의 외부화 구성
- 풀 기반을 많이 씀.
- 이때 많이 쓰는게 스프링 클라우드 라는 것임.
- 구성 서버는 다음 방식으로 구현 가능.
- 버전 관리 시스템
- sql &noSql
- 전용 구성 서버(스프링 클라우드 컨피그 서버)
- 풀 모델은 서비스 인스턴스가 시동 시 자신이 필요한 값을 구성 전용 서버에 접속하여 읽는 방식이다.

- 스프링클라우드 컨피그
- 원리는
- 서버에서 구성 프로퍼티를 가져와 스프링 ApplicationContext에 주입함.
- 원리는
- 장점
- 중앙화 구성
- 민감 데이터 투명 복호화
- 동적 재구성.
11.3 관측 가능한 서비스 설계
- 어플리케이션을 운영하려면, 다양한 지표들을 보고 징후를 판단하고 조치할수 있어야함.
- 그런데 그게 안된다면, 어찌 해야하는가?
__11.3.1 헬스 체크 API 패턴
- 헬스 체크 api를 구현했다면, 해당 api만 호출하면 정상 상태인지 파악이 가능해야함.
- 스프링 액추에이터를 이용하면 쉽게 기능 구현가능.
- 헬스 체크 API 패턴
- 서비스는 서비스 상태를 반환하는 GET /health 등의 헬스 체크 API 끝점을 표출한다. 헬스 체크 끝점을 구현한 코드는 인스턴스의 상태를 어떻게든 판단해야 한다. 이 끝점은 배포 인프라가 호출한다.
__11.3.2 로그 수집 패턴
- 로그 수집의 경우 하나의 공간으로 모으는 편
- ELK를 주로 쓴다.
- fluentD도 쓰는 편이긴 함.
- 로그 파이프라인과 로깅 서버는 보통 운영 팀이 담당하지만 유용한 로그를 남기는 코딩 작업은 서비스 개발자의 몫이다.
__11.3.3 분산 추적 패턴
- 분산 추적.
- 이 요청이 어디를 타고, 어디를 갔다가, 어디서 얼마나 걸렸고, 완료까지는 결국 몇초!
- 지금 회사는 ?
- 이런 것 부족함. 이런 정보의 관리를 하는 시스템 고려 필요.
- 외부 요청마다 유일한 ID를 하나씩 부여해서 서비스가 흘러가는 과정을 기록하고 시각화/분석 기능을 제공하는 중앙화 서버에 자료를 남긴다.
- 인스트루멘테이션 라이브러리는 스팬 트리를 만들어 분산 추적 서버로 보낸다. 관련 코드는 AOP를 이용해 관심사를 분리하여 구현하는 것이 바람직하다.
- AOP 프레임웍으로 알려진 스프링 클라우드 슬루스는 분산 추적 기능을 서비스에 자동 연계합니다. 이 라이브러리를 디펜던시로 추가하면 서비스가 분산 추척 API를 직접 호출 할 필요가 없다.
- 참고할 만한 자료 : 라인의 zipkin 활용기 기술 블로그 : https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin
__11.3.4 애플리케이션 지표 패턴
- 애플리케이션의
- 비즈니스적 지표
- 성능 지표
- 를 관리할 수 있어야한다.
- 지표들은?
- 여기 지표들은 스프링 엑추에이터에서 지원하는 매트릭을 이용하면 쉽게 구현할 수 있음.
__11.3.5 예외 추적 패턴
- 애플리케이션의 예외를 추적하는 패턴도 있음.
- 예외만 따로 관리하자!
- 이유 :
- 중복된 예외 제거
- 알림을 생성
- 예외 해결 과정을 관리.
- 이유 :
- 센트리라는 것을 많이 쓰더라.
- 허니베저도 많이 쓰는 편임.
__11.3.6 감사 로깅 패턴
- 감사 로깅이란 : 수행동작과 진행과정을 일일이 기록하는 것.
- 주로 방법..? :
- 메서드 호출 로깅 : 스프링 aop기능을 활용하여 가ㅡ능.
- 이벤트 소싱 방식 : 애그리거트의 변경 이벤트를 전부 기록.
- 쿼리는 따로 로깅해야한다는 것! 참고하자.
11.4 서비스 개발: 마이크로서비스 섀시 패턴
- 앞에서 본 여러 요소들 외에도 디스커버리, 회로 차단기 기능을 서비스에 추가 구현이 필요한데 이런 작업을 서비스를 개발 할 때마다 반복되는 것은 말이 안된다. 마이크로 서비스 새시 기반으로 서비스를 구축하면 횡단 관심사를 처리하는 코드를 서비스에 작성할 일이 거의 없고 개발 속도는 그만큼 빨라진다.

__11.4.1 마이크로서비스 섀시
- 예제 어플리케이션은 ** 스프링 부트, 스프링 클라우드를 마이크로 서비스 새시로 ** 활용한다. 서비스를 개발하는 모든 언어/플랫폼 조합마다 마이크로 서비스 새시가 하나씩 필요한 단점이 있다. 다행히도 마이크로 서비스 새시에 구현된 기능은 대부분 인프라에 대신 구현할 수 있다.
__11.4.2 이제는 서비스 메시로
마이크로서비스 새시는 언어마다 하나씩 필요하다는 단점이 있다. 이런 문제점 때문에 공통 기능 일부를 서비스 외부에 위치한 서비스 메시에 구현하게 된다. 서비스 메시는 한 서비스와 다른 서비스, 그리고 외부간의 소통을 조정하는 인프라로 서비스를 드나드는 트래픽은 모두 회로 차단기, 분산 추적, 서비스 디스커버리, 부하 분산, 룰 기반 트래픽 라우팅등 다양한 관심사가 구현된 서비스 메시를 통과한다.
- 서비스 메시 덕분에서 마이크로 서비스 새시는 외부화 구성, 헬스 체크등 애플리케이션 코드와 단단히 결합된 관심사만 구현하면 되기때문에 단순해진다.
- 마이크로 서비스 새시
- 마이크로서비스 섀시(Microservice Chassis)에서 “섀시”는 자동차의 프레임 역할을 하는 “Chassis”를 비유적으로 사용한 용어입니다. 자동차에서 섀시는 엔진, 바퀴, 서스펜션 등 주요 부품을 안정적으로 지지하고 연결하는 역할을 합니다. 마찬가지로, 마이크로서비스 섀시는 각 마이크로서비스의 공통적인 기능(예: 로깅, 모니터링, 보안, 구성 관리 등)을 지원하고, 마이크로서비스가 더 쉽게 구축되고 운영될 수 있도록 기본적인 틀과 구조를 제공하는 역할을 합니다.
- 영어로는 “Microservice Chassis”라고 부릅니다. “Chassis”라는 단어는 이처럼 시스템의 기초 구조나 프레임워크를 의미할 때 자주 사용됩니다.
서비스 매시를 제공하는 제품은 뭐가 있어?
- 링커드, 이스티오, 콘듀이트
- 링커드가 가장 성숙하다고 한다!
- https://linkerd.io/2.16/overview/
- 그런데 이것만 있으면 다는 아니다.
- 로그 수집같은것은 지원하지 않기 때문에 ELK, fluntd와 연동시켜야할수도 있다.
11.5 마치며
- 기능 만큼이나 안전하고, 구성 가능하며 관측 가능해야한다.
- 보안 : Oauth2.0으로 안전하게 지원하자!
- 런 타임에 구성 프로퍼티를 서비스에 공급하자!, 주로 풀 방식을 많이 쓰는데, java진영에서는 스프링 클라우드를 쓴다!
- 운영자와 개발자는 관측성 패턴을 구현할 책임을 분담한다.
- 개발을 단순화하려면 서비스를 마이크로서비스 섀시 기반으로 개발해보자!
참고자료
- https://freeend.tistory.com/116
- https://velog.io/@litien/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%8C%A8%ED%84%B4-%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EB%A0%88%EB%94%94-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B0%9C%EB%B0%9C
코드는
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class OrderService {
private Logger logger = LoggerFactory.getLogger(getClass());
private SagaInstanceFactory sagaInstanceFactory;
private OrderRepository orderRepository;
private RestaurantRepository restaurantRepository;
private CreateOrderSaga createOrderSaga;
private CancelOrderSaga cancelOrderSaga;
private ReviseOrderSaga reviseOrderSaga;
private OrderDomainEventPublisher orderAggregateEventPublisher;
private Optional<MeterRegistry> meterRegistry;

