하위항목
Akka 클러스터 선택
당신이 해야 할 아키텍처 선택은 마이크로서비스 아키텍처를 사용할 것인지 아니면 전통적인 분산 애플리케이션을 사용할 것인지입니다. 이 선택은 당신이 Akka Cluster를 어떻게 사용해야 하는지에 영향을 미칠 것입니다.
상태 저장 또는 상태 비저장 애플리케이션: Akka 클러스터 사용 여부에 대한 비디오는 Akka 클러스터를 사용하려는 동기를 이해하는 데 좋은 시작점입니다.
마이크로서비스
마이크로서비스는 여러 매력적인 속성을 가지고 있는데, 예를 들어 마이크로서비스의 독립적인 특성으로 인해 더 작고 집중적인 여러 팀이 새로운 기능을 더 자주 제공하고 비즈니스 기회에 더 빠르게 대응할 수 있습니다. 반응형 마이크로서비스는 Jonas Bonér가 저서 Reactive Microsystems: The Evolution of Microservices at Scale 에서 지적한 대로 격리되고 자율적이어야 하며 단일 책임이 있어야 합니다 .
마이크로서비스 아키텍처에서는 서비스 내부 통신과 서비스 간 통신을 고려해야 합니다.
일반적으로 우리는 Akka Cluster와 다른 서비스 간의 액터 메시징을 사용하지 않는 것을 권장합니다. 왜냐하면 그렇게 하면 서비스 간의 코드 결합이 너무 팽팽해지고 이를 서로 독립적으로 배포하는 데 어려움이 생기기 때문입니다. 이것이 마이크로서비스 아키텍처를 사용하는 주된 이유 중 하나입니다. 이에 대한 배경 정보는 내부 및 외부 커뮤니케이션 에 대한 논의를 참조하세요.
단일 서비스의 노드(집합적으로 클러스터라고 함)는 분리가 덜 필요합니다. 이들은 동일한 코드를 공유하고 단일 팀이나 개인이 세트로 함께 배포합니다. 롤링 배포 중에 두 버전이 동시에 실행될 수 있지만 전체 세트의 배포에는 단일 제어 지점이 있습니다. 이러한 이유로 서비스 내 통신은 Akka 클러스터, 장애 관리 및 액터 메시징을 활용할 수 있으며, 이는 사용하기 편리하고 성능이 뛰어납니다.
서로 다른 서비스 간에는 동기식(그러나 비차단) 통신에 Akka HTTP 또는 Akka gRPC를 사용할 수 있으며, 비동기식 통신을 통합하기 위해 Akka Streams Kafka 또는 다른 Alpakka 커넥터를 사용할 수 있습니다. 이러한 모든 통신 메커니즘은 엔드투엔드 백프레셔가 있는 메시지 스트리밍과 잘 작동하며, 동기식 통신 도구는 단일 요청 응답 상호 작용에도 사용할 수 있습니다. 이러한 도구를 사용할 때 통신의 양쪽을 Akka로 구현할 필요가 없고, 프로그래밍 언어도 중요하지 않다는 점도 중요합니다.
기존의 분산형 애플리케이션
마이크로서비스가 많은 새로운 과제를 도입한다는 사실과 애플리케이션을 구축하는 유일한 방법은 아니라는 사실을 인정합니다. 기존의 분산 애플리케이션은 복잡성이 적고 많은 경우 잘 작동할 수 있습니다. 예를 들어, 단일 팀으로 구성된 소규모 스타트업의 경우 출시 시간이 모든 것이 되는 애플리케이션을 구축합니다. Akka Cluster는 이러한 분산 애플리케이션을 구축하는 데 효율적으로 사용할 수 있습니다.
이 경우 단일 코드 기반(또는 모듈화를 위한 기존 바이너리 종속성 관리 사용)에서 빌드된 단일 배포 단위가 있지만 단일 클러스터를 사용하여 여러 노드에 배포됩니다. 배포 및 제어의 중앙 지점이 있으므로 더 긴밀한 결합이 괜찮습니다. 어떤 경우에는 노드에 클러스터가 완전히 동질적이지 않은 특수 런타임 역할이 있을 수 있습니다(예: "프런트엔드" 및 "백엔드" 노드 또는 전용 마스터/워커 노드). 그러나 이러한 노드가 동일한 빌드된 아티팩트에서 실행되는 경우 이는 런타임 동작일 뿐이며 완전히 별도의 아티팩트의 긴밀한 결합에서 발생할 수 있는 동일한 종류의 문제를 일으키지 않습니다.
밀접하게 결합된 분산 애플리케이션은 수년간 업계와 많은 Akka 사용자에게 도움이 되었으며 여전히 유효한 선택입니다.
파편화된 모놀리스
때때로 "파편화된 모놀리스"라고 불리는 안티 패턴도 있습니다. 서로 독립적으로 빌드되고 배포되는 여러 서비스가 있지만, 공유 클러스터, 서비스 API 호출에 대한 공유 코드 및 종속성 또는 공유 데이터베이스 스키마와 같이 이를 매우 위험하게 만드는 긴밀한 결합이 있습니다. 코드와 배포 단위의 물리적 분리로 인해 자율성에 대한 잘못된 감각이 있지만, 한 서비스의 구현 변경 사항이 다른 서비스의 동작으로 누출되어 문제가 발생할 가능성이 높습니다.
이런 상황에 처한 조직은 종종 여러 서비스의 배포를 중앙에서 조정하려고 시도하여 대응하는데, 이때는 마이크로서비스의 주요 이점을 잃고 비용을 떠안게 됩니다. 실제로 분리할 수 없는 것들을 별도로 빌드하고 배포하는 중간 상태에 있습니다. 어떤 사람들은 이렇게 하고, 어떤 사람들은 이를 작동하게 만들지만, 이는 권장할 만한 일이 아니며 신중하게 관리해야 합니다.
그래서 초기비용과 트래픽이 발생해 비대해지는 비용은 단순하게 모롤리식 VS MSA가아닌 초기에 설계를 고려했는지 여부입니다.
Akka Cluster의 경우 모놀리식으로도 구성이 가능하며 필요하면 분리작동 시킬수 있습니다. 여기서는 모놀리식이란 표현보다
경계를 구분하고 StandAlone 구성이 가능하며, MSA가 아니여도 설계에 따라 MSA/SOA로 확장가능한 구성을 시도해보겠습니다.
StandAlone Cluster
akka { actor { provider = "cluster" serializers { jackson-json = "akka.serialization.jackson.JacksonJsonSerializer" jackson-cbor = "akka.serialization.jackson.JacksonCborSerializer" proto = "akka.remote.serialization.ProtobufSerializer" } serialization-bindings { "com.example.kotlinbootlabs.actor.PersitenceSerializable" = jackson-json } } remote.artery { canonical { hostname = "127.0.0.1" port = 2553 } } cluster { seed-nodes = ["akka://StandAloneSystem@127.0.0.1:2553"] cluster.roles = ["seed", "helloA","helloB"] downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" } }
Role단위가 서비스이면 SOA를 기준으로 확장가능하고 기능단위이면 MSA 에 가까울수 있습니다.
Role이 어떠한 구성을 가지게할지 설계의 원칙에 따라 구성되어 지며
RoleB의 트래픽이 증가해 분리가 필요한 경우 독립된 구성으로 작동시킬수도 재구성을 할수도 있습니다.
Cluster 확장
AKKA 클러스터 특징
- 물리적 주소를 알필요없이 ActorA에게 명령을 보내고 ActorA는 ActorB와 통신할수 있습니다. 이러한 특징을 위치 투명성있는 사용성이라고 표현하며 마치 하나의 앱처럼 논리적 구성만 이용합니다.
- SEED노드는 업데이트가 될 필요가 없음으로 주키퍼와같이 분리 구성되는것이 권장되지만, 클러스터의 Join을 관리하는 SEED 역할을 누구든 수행할수 있습니다.
- MSA단위로 레파지토리가 추가되어야하고 새로운 CI/CD 구성을 필요로 하지 않습니다. 동일 배포이미지로 , 설정 변경으만으로 다르게 작동할수 있으며 쿠버클러스터와 호환성도 제공합니다.
- StandAlone으로 구성가능하며 필요하면 분리할수도 있는 , 레파지토리별 CICD를 요구해 비용이 급증하는 MSA와 다른 차이점을 가지고 있는 클러스터 시스템입니다.
- Pub/Sub 분산처리 EventBUS를 포함 다양한 분산메시징을 지원합니다.
다음과 같이 구성코드의 변경으로 다른 Role을 가진체 작동할수 있습니다.
cluster { seed-nodes = ["akka://ClusterSystem@127.0.0.1:2551"] cluster.roles = ["seed", "helloA"] downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" }
cluster { seed-nodes = ["akka://ClusterSystem@127.0.0.1:2551"] cluster.roles = ["helloB"] downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" }
클러스터 시스템생성
val cluster1 = ConfigFactory.load("cluster1.conf") val cluster2 = ConfigFactory.load("cluster2.conf") val standalone = ConfigFactory.load("standalone.conf") testKitA = ActorTestKit.create("ClusterSystem",cluster1) testKitB = ActorTestKit.create("ClusterSystem",cluster2) standAlone = ActorTestKit.create("StandAloneSystem",standalone)
- Cluster를 2개 구성할수도 있으며 단독(StandAlone) 으로 구성할수 있습니다. ( Local에서 멀티 구성 테스트 )
분산처리 Pub/Sub
// SUB 샘플 var mediator = DistributedPubSub.get(context.system).mediator() mediator.tell(DistributedPubSubMediator.Subscribe("roleA", Adapter.toClassic(context.self)),null) // PUB 샘플 and 수신 테스트 val mediator = DistributedPubSub.get(standAlone.system()).mediator() val probeA = standAlone.createTestProbe<HelloActorAResponse>() val probeB = standAlone.createTestProbe<HelloActorBResponse>() mediator.tell(DistributedPubSubMediator.Publish("roleA", HelloA("Hello", probeA.ref)), null) mediator.tell(DistributedPubSubMediator.Publish("roleB", HelloB("Hello", probeB.ref)), null) probeA.expectMessage(HelloAResponse("Kotlin")) probeB.expectMessage(HelloBResponse("Kotlin"))
- 전통적인 Pub/Sub을 액터에 탑재해 활용할수 있습니다.
- 유닛테스트를 지원하는것은 보너스입니다.
Redis에서 지원하는 전통적인 PubSub 외에, 아래와같은 다양한 클러스터 기능을 제공합니다.
Akka는 분산 상태 처리를 위해 다음과 같은 클러스터 관련 도구들을 제공합니다:
Cluster Sharding: 여러 노드에 걸쳐 액터를 분산시키고, 물리적 위치에 상관없이 논리적 식별자를 사용하여 액터와 상호 작용할 수 있게 해줍니다. Cluster Sharding
- kafka 파티션 전략과 유사
Cluster Singleton: 클러스터 내 어딘가에 특정 유형의 액터가 하나만 실행되도록 보장합니다. Cluster Singleton
- 단 하나의 처리기가 필요한 경우 중복처리 없이 이중화 구성할수 있습니다. 정산배치 처리기와같이 단하나만 작동해야하는 경우 활용
Distributed Data: Akka 클러스터의 노드 간에 데이터를 공유할 때 유용합니다. 키-값 저장소와 유사한 API를 제공하는 액터를 통해 데이터에 접근합니다. Distributed Data
- redis 분산 저장을 유사
Distributed Publish Subscribe: 클러스터 내 액터 간 토픽 기반의 발행-구독 메시징을 지원합니다. 송신자는 수신 액터가 어느 노드에서 실행 중인지 알 필요가 없습니다. Distributed Publish Subscribe
- redis가 제공하는 pub/sub 유사
Cluster aware routers: 라운드 로빈이나 일관된 해싱과 같은 라우팅 전략을 사용하여 클러스터의 다른 노드에 있는 액터들에게 메시지를 분산시킵니다. Cluster aware routers
- rabbit mq의 라우트 분배 기능유사
클러스터 로깅
[01:39:26.984] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.remote.artery.ArteryTransport - Remoting started with transport [Artery tcp]; listening on address [akka://ClusterSystem@127.0.0.1:2551] with UID [-3813521455754216397] [01:39:26.994] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2551] - Starting up, Akka version [2.7.0] ... [01:39:27.056] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2551] - Registered cluster JMX MBean [akka:type=Cluster,port=2551] [01:39:27.057] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2551] - Started up successfully [01:39:27.165] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.cluster.sbr.SplitBrainResolver - SBR started. Config: strategy [KeepMajority], stable-after [20 seconds], down-all-when-unstable [15 seconds], selfUniqueAddress [akka://ClusterSystem@127.0.0.1:2551#-3813521455754216397], selfDc [default]. [01:39:27.185] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2551] - Node [akka://ClusterSystem@127.0.0.1:2551] is JOINING itself (with roles [dc-default], version [0.0.0]) and forming new cluster [01:39:27.188] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2551] - is the new leader among reachable nodes (more leaders may exist) [01:39:27.210] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2551] - Leader is moving node [akka://ClusterSystem@127.0.0.1:2551] to [Up] [01:39:27.253] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.cluster.sbr.SplitBrainResolver - This node is now the leader responsible for taking SBR decisions among the reachable nodes (more leaders may exist). [01:39:27.510] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.event.slf4j.Slf4jLogger - Slf4jLogger started [01:39:27.532] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.remote.artery.ArteryTransport - Remoting started with transport [Artery tcp]; listening on address [akka://ClusterSystem@127.0.0.1:2552] with UID [-4810156408685944688] [01:39:27.532] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2552] - Starting up, Akka version [2.7.0] ... [01:39:27.534] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2552] - Registered cluster JMX MBean [akka:type=Cluster,port=2552] [01:39:27.534] [ClusterSystem-akka.actor.default-dispatcher-5] INFO akka.cluster.Cluster - Cluster Node [akka://ClusterSystem@127.0.0.1:2552] - Started up successfully [01:39:27.536] [ClusterSystem-akka.actor.default-dispatcher-3] INFO akka.cluster.sbr.SplitBrainResolver - SBR started. Config: strategy [KeepMajority], stable-after [20
- 우리가 만든 어플리케이션을 네트워크로 클러스터를 하려면 고급 네트워크 프로그래밍을 해야하지만~ 설정만으로 클러스터로 묶이며 단일지점 병목이 없는 P2P 로 상호 조인되고 연결되며 고성능 프로토콜을 이용하며( netty와 유사하거나 성능이 좋음 , 고성능 프로토콜 채택가능)
이러한 방식때문에 외부 MQ를 이용하는것보다 전송 성능이 월등하며 브로드 캐스팅을 포함 더 디테일한(액터참조) 단위까지 논리적 전송범위를 설계할수 있습니다.- 참고 : https://www.lightbend.com/blog/benchmarking-kafka-vs-akka-brokerless-pub-sub
- Redis/Rabbit MQ와도 월등하게 높음
- 참고 : https://www.lightbend.com/blog/benchmarking-kafka-vs-akka-brokerless-pub-sub
이러한 도구들은 Akka 클러스터 환경에서 분산 상태를 효과적으로 관리하고 처리할 수 있게 해줍니다. 이 기종간 메시지 브로커가 필요한 경우 Kafka또는 RabbitMQ , 상태 저장이 필요한 경우 Redis 다양한 외부 단일지점의 클러스터를 이용해야 할수도 있지만
AKKA 클러스터는 어플리케이션 자체에서 연합 구성되어 분산처리 툴을 제공하기때문에 분산처리에 필요한 외부 구성요소를 최소화 할수 있습니다.
어플리케이션이 활용하지 않는 POD 가용성을 높일수 있으며 그로인해 단일지점 병목을 줄일수 있습니다.
다양한 외부요소와 Reactive Stream 방식으로 연동할수 있는 엣지기능도 휼륭하게 제공하기때문에 클러스터내 특정기능 요소를 외부와 연결해 사용할수도 있습니다.
다음장에서는 클러스터가 제공하는 유용한 툴들을 하나씩 살펴보겠습니다.
지속 추가될 구현및 테스트 코드
- https://github.com/psmon/java-labs/tree/master/KotlinBootLabs/src/test/kotlin/com/example/kotlinbootlabs/actor/cluster
- https://doc.akka.io/libraries/guide/concepts/ddd.html
- AKKA를 통한 MSA 설계 원칙으로 DDD를 채택 ( AKKA와 상관없이 분산처리에 도전할시 권장 도서 )
NEXT : 클러스터내 액터위치 투명성