Table of Contents
Reactive Programming
- 이벤트가 발생하거나 상태가 변했을 때 특정 코드를 실행하는 프로그래밍 방식
- 이벤트를 발생하거나 상태가 변하는 대상을 구독하는 형태로 구현한다.
RxJava
의 Observable, Reactor
의 Mono나 Flux, Coroutine
의 Flow, Android의 LiveData
가 리액티브 프로그래밍의 예다. - 보통 구독을 하면서 콜백을 함께 전달한다.
Event Driven Architecture
- 이벤트가 발생했을 때 특정 작업을 수행하도록 설계된 아키텍처
- MSA에서는 독립적이고 작은 여러 모듈이 서로 상호작용하는데, 이 때문에 특정 모듈에서 이벤트가 발생했을 때 다른 모듈의 로직을 실행해야하는 경우가 빈번하다.
- 특히 여러 모듈에서 발생하는 데이터를
Kafka
, Redis
같은 메시지 큐에 집중화하고, 이를 구독하는 모듈에게 이벤트를 발생시키는 형태로 이벤트 기반 아키텍처를 구현할 수 있다.
Domain Driven Design
- 도메인 주도 설계
- 도메인, 현업의 비즈니스를 중심으로 하는 프로그래밍
- 기존에는 기획팀, 디자인팀, 개발팀, 인프라팀, 도메인 팀으로 구성되었다면 DDD는 도메인 단위로 팀을 구성한다.
- 예를 들어 결제와 관련된 기능을 구현할 때 관련된 기획자, 디자이너, 프론트엔드 개발자, 백엔드 개발자가 한 팀을 구성한다.
- 팀이 도메인 단위로 움직이므로 서비스도 도메인 단위로 관리할 수 있는 MSA가 적합하다.
마이크로서비스 아키텍처
- 모놀리식 아키텍처에 대응되는 아키텍처로 하나의 큰 시스템을 독립적이고 작은 모듈로 분리하고 서로 약한 결합으로 묶여 상호작용하는 아키텍처
- MSA의 가장 큰 장점은 수평적 확장성이다.
- 개발이 복잡하다. 모듈을 독립적으로 나누는 것도 어렵고, 모듈 하나에 장애가 발생했을 경우에 대한 방어 코드도 작성해야한다. 그 밖에도 테스트와 디버깅이 어렵다.
- Multiple Database 및 트랜잭션 관리가 어려울 수 있다.
- 도커 같은 컨테이너 가상화 기술과 쿠버네티스 같은 오토 스케일링, 셀프 힐링, 트래픽 분배 등의 기능을 제공하는 오케스트레이션 도구가 필요하다.
- 또한 여러 모듈에서 생성되는 데이터를 중앙에서 관리하고, 다른 모듈로 전파될 수 있도록 Kafka, Redis 같은
Publis/Subscribe
모델의 메시지 큐도 필요하다. - 그리고 중앙 집중형 로그 관리를 위해 ELK + Beats 같은 서비스도 필요하다.
- 그 외에도 모듈 간 통신이 빈번하기 때문에 기존 REST API 통신보다 더 빠른 통신 기술이 필요하며,
gRPC
가 사용되기도 한다.
Apache Kafka
Publish/Subscribe
모델의 메시지 큐 - 분산 코디네이터인
Zookeeper
와 브로커 서버인 Kafka
여러 대로 클러스터를 구축할 수 있다. Key-Value
형식으로 메시지를 전송할 수 있다. Producer
는 메시지를 생산하는 주체 Consumer
는 메시지를 소비하는 주체 - 메시지를
Topic
단위로 분류한다. - Topic에 여러
Partition
을 할당하여 병렬적으로 데이터를 생산할 수 있다. Consumer Group
을 할당하여 배압 이슈를 해결할 수 있다. Consumer Group의 각 Consumer
는 서로 다른 Partition
에서 병렬적으로 데이터를 읽어온다. - 기존 메시지 큐가 인메모리 방식으로 데이터를 저장하는 반면,
Kafka
는 온디스크에 데이터를 저장하기 때문에 영속성이 보장이 된다. Replication
을 활용하여 Topic을 여러 노드에 분산 저장하면 고가용성을 높일 수 있다.
gRPC
- google Remote Procedure Call
- 다른 서버의 메서드를 로컬 메서드처럼 호출하는 기술
프로토콜 버퍼
를 사용하는 인터페이스 프로젝트를 생성하고, .proto
확장자로 인터페이스 코드를 작성한 후 JAR 파일로 빌드한다. - 이 결과물을 클라이언트 프로젝트와 서버 프로젝트에 임포트하여 사용할 수 있다.
- REST API는 HTTP 1.1을 사용하고 메시지 포맷으로 JSON을 사용한다.
- gRPC는 HTTP 2를 사용하고 메시지 포맷으로 프로토콜 버퍼를 사용한다.
- gRPC는 REST API에 비해 형식이 더욱 엄격하여 불필요한 논쟁을 줄인다.
- gRPC의 프로토콜 버퍼는 REST API의 JSON 직렬화보다 최대 8배 빼를 수 있다고 한다.
- 이러한 특성 때문에 MSA 환경에서 서비스 간 통신에 사용된다.
Docker
- 호스트 OS에
Docker container
라는 격리된 공간을 제공해주는 컨테이너 가상화 기술 Docker Container
를 Docker Image
로 빌드하면 도커 엔진이 설치된 어느 곳에든 쉽게 배포할 수 있다. - 또한 도커 엔진만 설치되어있으면 개발 환경과 동일한 환경을 쉽게 구성할 수 있다.
Kubernetes
같은 컨테이너 오케스트레이션 기술
과 함께 사용하여 MSA(Microservice Architecuture)
를 쉽게 구성할 수 있다. - 가상머신은 게스트 OS와 하이버바이저를 거쳐 호스트 OS의 커널과 자원을 사용하기 때문에 성능 손실이 발생한다. 반면 Docker는 하이버파이저와 게스트 OS가 없고 도커 엔진만을 거쳐 호스트 OS의 커널과 자원을 사용하며, 호스트 OS에서 프로세스로 동작하기 때문에 속도가 훨씬 빠르다
Dockerfile
: 도커 이미지를 생성하기 위한 스크립트 파일, docker build <IMAGE_NAME>/<IMAGE_TAG> .
Kubernetes
- 컨테이너 오케스트레이션 플랫폼으로 여러 물리적 서버를 논리적으로 하나의 서버처럼 클러스터링해준다.
- 쿠버네티스를 사용하면 클러스터에 노드를 쉽게 추가하여 Scale-out 할 수 있다.
- 온프레미스나 EC2 같은 클라우드에도 클러스터를 구축할 수 있으며, AWS EKS 같은 관리형 쿠버네티스를 사용할 수도 있다.
- 클러스터에 노드를 쉽게 추가할 수 있어 수평적 확장이 간단하다.
- 레플리카셋으로 팟을 오토 스케일링할 수 있어 수평적 확장이 가능하며, 셀프 힐링 기능으로 일정 수의 팟을 유지할 수 있다.
- 디플로이먼트로 무중단 배포가 가능하며, 이전 버전으로 쉽게 롤백할 수 있다.
- 서비스로 로드밸런싱이 가능하다.
- 그 외에도 볼륨, 네트워크, 보안 등 다양한 기능을 제공한다.
- 쿠버네티스
오브젝트
는 다음과 같다.
노드
- 쿠버네티스 클러스터를 구성하는 호스트 하나
- 여러 노드 중 하나를 마스터 노드로 설정하고 나머지를 워커 노드로 설정한다.
팟
- 쿠버네티스에서는 컨테이너 애플리케이션의 기본 단위를 팟(Pod)이라고 한다.
- 팟은 하나 이상의 도커 컨테이너로 구성된다.
레플리카셋
- 동일한 팟 여러 개를 쉽게 생성하고 관리할 수 있다.
- 지정된 팟의 개수를 자동으로 유지하는 셀프 힐링을 제공한다.
디플로이먼트
- 여러 팟을 롤링 업데이트하여 무중단 배포가 가능하게 한다.
- 이전 배포로 쉽게 롤백할 수 있다.
서비스
- 팟은 기본적으로 쿠버네티스 클러스터 내부에만 노출된다.
- 서비스를 사용하면 팟을 클러스터 외부로 노출시킬 수 있다.
네임스페이스
- 여러 오브젝트를 논리적으로 구성할 수 있다.
- 예를 들어
nginx-ingress
와 관련된 오브젝트는 nginx-ingress
네임스페이스에 위치한다. ArgoCD
와 관련된 오브젝트는 argocd
네임스페이스에 위치한다. - 네임스페이스 별로 다른 컨피그 맵과 시크릿을 적용할 수 있다.
컨피그맵
- 네임스페이스 별로 환경변수를 설정할 수 있다.
시크릿
- 네임스페이스 별로 암호화된 환경변수를 설정할 수 있다.
인그레스
- 팟 외부 노출
- 서비스를 URL Path를 통한 포워딩
- SSL/TLS를 제공하는 오브젝트다.
- 인그레스 컨트롤러를 설치해야한다.
Kustomize
- 선언적, 동적으로 쿠버네티스
yml
설정파일을 생성할 수 있다.
Argo CD
GitOps
환경에서 메니페스트 리포지토리의 Kustomize
파일이 변경되면 이를 감지하여 쿠버네티스에 반영하는 역할을 한다.
Web Socket
- 양방향 데이터 통신을 위한 프로토콜
- TCP Socket이 OSI 4 Layer에서 동작하는 반면 WebSocket은 OSI 7 Layer의
HTTP
프로토콜 위에서 동작한다. - 먼저 HTTP 프로토콜로 Websocket Handshaking을 하여 연결을 수립하고, 그 다음은 Websocket 프로토콜로 양방향으로 통신한다.
- WebSocket 위에서 동작하는
STOMP
를 사용하면 Publish/Subscribe 모델로 1:N 브로드캐스팅을 쉽게 구현할 수 있다. 이를 통해 같은 채팅방의 모든 사용자에게 메시지를 보내는 기능을 구현할 수 있다. 이때 웹 소켓서버는 메시지 브로커 역할을 하게 된다. 또한 STOMP
를 사용하면 WebSocket에서 전달되는 메시지의 포맷을 고정할 수 있다. - 주식 앱처럼 실시간 1:1 통신이 필요한 경우
WebSocket
으로 충분하며, 채팅방처럼 1:N 브로드캐스팅 기능이 필요할 때는 STOMP
기능을 사용할 수 있다. - 참고로 웹소켓 서버는
node.js
로도 구현이 가능하다. 특히 자바스크립트 진영에는 웹소켓 서버 위에서 동작하는 socket.io
자바스크립트 라이브러리를 사용하여 웹소켓 서버를 쉽게 구현할 수 있다. socket.io
역시 Java 진영의 STOMP
처럼 브로드캐스팅 기능을 지원하며 사용하기가 상당이 쉽다. 또한 클라이언트 쪽에도 socket.io
을 위한 클라이언트 라이브러리가 잘 활성화되어있기 때문에 클라이언트 개발자는 socket.io
를 선호한다고 들었다. 웹 환경에서는 socket.io-client
, 안드로이드 앱에서는 socket.io-client-java
, iOS 앱에서는 socket.io-client-swift
라이브러리를 사용하면 된다.