소프트웨어 개발이라는 분야는 오래했다고 잘한다는 것이 보장되는 곳이 아닙니다.

끊임없이 Why 와 How 에 대해서 스스로 질문하고 공부해야 성장하는 분야인 것 같습니다.

 

1. 아키텍트 vs 개발자

 

요즘 들어서 "개발자"라는 직업이 아주 핫합니다.

그런데 IT시장에서 오래 일하신분들이 "개발자" 라는 호칭을 들었을 때 반응이 좀 다릅니다.

 

"언제까지 개발만 할거냐?" 라는 질문을 던지며 전체적인 그림을 그리는 아키텍트가 되어야 한다는 사람들이 많이 있습니다.

이런분들은 개발=코딩의 의미로 바라보는 것 같습니다.

 

그러나 "우리는 아키텍트라는 표현을 사용하지 않아. 전부 다 개발자이지" 라고 이야기하는 분들도 상당히 많습니다.

보통 이런회사의 개발자분들은 아키텍처, 설계, 개발, 테스트를 모두 합쳐서 개발로 인식하여 수행합니다.

 

비율을 정확히 따져보지는 않았지만 두 부류 모두 부르는 명칭만 다를 뿐이지 사실 해야하는 일은 비슷합니다.

 

고객과 시장의 요구사항을 만났을 때, 기술적 관점으로 그것을 재해석하여 소프트웨어로 구현해야 하는 것이 궁극적인 목표입니다.

 

 

- 아키텍처 그림만 PPT로 열심히 그리는 사람은 아키텍트가 아닙니다. 그림은 아무나 그릴 수 있습니다.

- 자기가 만드는 프로그램 하나의 개발에만 집중하는 사람은 좋은 개발자가 아닙니다.

   전체적인 구성요소와 관계를 파악하여 기능적/비기능적 요구사항을 파악하고 이에 따른 제약사항과 구현방안을 설계/개발 할 수있는 사람이 좋은 개발자입니다.

 

2. 기술 vs 기본

 

시장의 많은 개발자들끼리 만났을때 심심치 않게 벌어지는 논쟁입니다.

 

"Spring 3.0에서는 어떠했고, 4.0에와서는 어떤 것이 바뀌었으며 ..."

"그럴때는 Spring 의 PostProcessor를 사용해서 구현하고"

등등 시장에서 사용되는 최신기술들에 관심이 많은 분들입니다.

 

이런 분들의 특징은 여러가지 기술등을 효과적으로 활용하여 원하는 바를 빠르게 만들어냅니다.

이른바 "손이 빠릅니다".

 

"이렇게 하면 시간복잡도, 공간복잡도가 이렇게 되고.."
"O(N^2)을 O(NlogN)으로 줄일 수 있고..

"건별 Data가 몇 byte이니, 초당 건수를 고려하면 메모리를 bytes만큼 사용하고"

 

이런 분들은 뭔가 하나에 꽂히면 파고들어서 최적의 구현을 하는 것이 목표입니다.

계속 들여다보고 최적화 / 효율화를 반복합니다. 

그리고 "흐뭇해 하십니다"

 

결론부터 말씀드리면 둘 다 중요합니다!

 

오픈 소스의 홍수 속에서 너무나도 많은 "바퀴"들이 존재합니다. 바퀴를 다시 만들필요는 없습니다.

망치가 있는데 돌멩이로 못질을 할필요는 없죠.

그런데 핵심이 되는 원리는 비슷합니다.

 

- 내가 사용하는 모듈의 시간복잡도, 공간복잡도가 어떠한지

- 내부에서 어느부분이 동기/비동기 처리가 일어나는지 

- Input 이 100건일때와 100만건일때 속도차이는 어떠한지

 

이러한 내용을 파악하지 않고 바퀴를 가져다 만드는데만 집중하면 밸런스가 안맞습니다.

시스템이 작을때는 문제가 없지만 데이터가 늘어나고 사용자가 늘어날 수록 버티기가 힘들어집니다.

 

이름있는 Tech기업들이 개발자(소프트웨어 인력)을 채용할때 알고리즘 테스트를 하는 것은 다 이유가 있습니다.

새로운 기술이 나왔을때 빨리 습득하고, 논리적 문제해결을 바탕으로 접근하여 그 기술을 사용할줄 아는 사람이 필요합니다.

 

 

현장에서 오래 일하다보면 고객의 요구사항을 만족시키는게 급급하여 기본을 무시하는 사람들을 많이 봅니다.

"알고리즘 같은건 그냥 시간있을때나 공부하는거지"

"그게 뭐가 중요해. 내가 하고 있는 업무랑 연관 없는데"

시스템이 커지고 복잡해지면 반드시 문제가 생깁니다.

 

한편으로는 기술을 무시하는 사람도 많습니다.

"남에 만든거 가져다 쓰는게 뭐 대단하다고"

"내가 만들면 그거보다 잘 만드는데"

바퀴를 만드는 것도 중요하지만 자동차를 만드는 것도 중요합니다. 사용자가 없는 소프트웨어는 죽은 겁니다.

 

이번 카테고리에서는 이 두가지를 섞어서 글을 정리해보려고 합니다.

개발과 알고리즘은 떼려야 뗄 수 없는 관계임을 우리는 이미 알고 있습니다.

이러한 주제를 아키텍처 레벨로 확장시켜서 살펴보려고 합니다.

 

우리가 알고있는 자료구조와 알고리즘들이 아키텍처에서는 어떻게 적용되고 있는지를 자세히 공부한다면

위와 같은 논쟁도 많이 없어지지 않을까 하는 생각입니다.

 

 

ps)

3. Design Pattern & Refactoring

위의 주제와는 약간 다른 주제입니다.

급변하는 요구사항을 어떻게 하면 최소한의 비용으로 빠르고 정확하게 만족시킬 수 있을지에 대한 해결방안으로 보면됩니다.

 

최근 Reactive Programing이라는 개념이 많이 사용되고 있어서 관련하여 개념들을 정리를 해보려고 한다.

1. Event Driven

Reactive를 알기 위해서 먼저 Event Driven을 알아볼 필요가 있다.
Event Driven은 말 그대로 프로그램을 만들때 Event를 기반으로 동작하도록 하는 것이다.
가장 대표적으로 많이 사용되는 형태가 Event Bus 를 활용하는 것이다.

Apache Kafka가 마이크로 서비스 구축에서 대표적으로 Event Bus의 역할을 하고 있다고 볼 수 있다.
간단히 설명하면 다음과 같다.

<그림-1>

UI의 mouse clickm, key in등을 포함하여 (타시스템이 오는 Event가 될 수도 있다.) 어떤 Event가 발생했을 때 담당 Application은 해당 요청을 직접 보내고 응답을 기다리는 것이 아니라 Event를 생성하여 저장만 한다. 이후 그 Event에 대한 처리는 다른 Application에서 가져가서 처리하게 된다. 기본적으로 비동기식의 처리흐름을 갖게 된다.


2. Actor Model (Akka)

 위와 같은 환경에서 결국 Event Bus(혹은 큐)를 기준으로 Event생성자와 소비자는 분리되어 동작하며 불필요한 대기현상을 없애는 것이 핵심. 그리고 이때 병렬프로그래밍을 활용하게 되는데 동시성제어를 용이하게 만든 컨셉으로 Actor Model을 사용하기도 한다. Akka는 이를 이용하여 프로그래밍을 쉽게 할 수 있도록 한 프레임워크이다.

Actor Model의 핵심은 다음과 같다.

- 메시지를 보내고 잊는다.

- 메세지를 받는다.

기존의 동기식 처리도 가능하지만 비동기처리가 일반적으로 권장된다.


<장점>

- Scale-out 이 쉽다.

- lock, synchronized 등을 사용하지 않아도 된다. 

- 단순하다. 메시지를 보낸다/받는다


<단점> 

- 전체적인 로직의 파악이 어렵다. (코드 추적이 어렵다)

- 시스템 사용량이 많다. (쓰레드를 많이 생성한다.)

- 상태를 갖지 않는다. (장점이자 단점)

- 여전히 DeadLock이 발생할 여지가 있다.   


3. 동기 vs 비동기

 동기식 : 호출자는 상대방이 결과값 혹은 Exception을 줄때까지 이후 작업을 진행하지 않고 대기한다.

 비동기식 : 호출자는 상대방의 응답에 관계없이 호출 후 작업을 진행한다.

                 응답처리는 callback, Future 등을 통해서 처리한다.


간혹 동기 vs 비동기, Non-blocking vs Blocking, 싱글쓰레드 vs 멀티쓰레드의 개념을 헷갈리거나 혼재해서 사용하는 경우가 있는데 반드시 분리가 필요하다. (Non-blocking IO 는 또다른 주제이다.)


위와 같이 비동기 방식을 사용하여 Event 혹은 메시지를 생성/소비 하며 Applicatino이 동작하는 것이 Event Driven이라고 볼 수 있으며 현재 Kafka, Akka를 사용하여 많이 구성되고 있다.

Kafka를 제외하고 Akka로만 구성하는 경우도 있는데 내부에 Message Queue, MailBox등을 가지고 있기 때문에 가능하다. (권장하진 않는다)


4. Reactive programming

wikipedia의 정의에 따르면 다음과 같다.

reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change

핵심은 모든 것을 Data streams로 간주하여 처리하는데 이 흐름이 비동기로 처리된다는 것이다.

여기까지 보면 Event Driven 과 유사해보이는데 좀 더 자세히 살펴보면 반응형이라는 것이 추가된다.

검색창에 타이핑을 할 때 자동완성 검색어가 표시되거나 웹 페이지상의 표현내용이 새로고침없이 실시간으로 바뀌는 것이다.


<그림-2>

이를 구현하는 핵심은 비동기와 Observer  패턴이다.

- 어떠한 정보를 주고 받을때 비동기적으로 DataStream의 형태로 처리를 하여 메인작업에 영향을 주지 않아야 하며

- 값에 대해서 즉각 반응을 하기 위해서 Observer패턴으로 값을 관찰하여 연산을 수행한다.


쉽게 사용할 수 있도록 여러가지 라이브러리가 존재한다. (Rx... )


사실 이것보다 더 중요한 차이가 있다고 생각하는데

Reactive는 데이터의 흐름을 중심으로 프로그래밍을 한다는 것이다. 

과거에 Control,제어의 흐름을 중심으로 프로그래밍을 해왔다면 최근에는 Data의 흐름을 중심으로 프로그래밍하는 것으로 추세가 변하고 있다.

객체가 주고받는 메시지에 집중하고, 함수에 들어가는 In,Out에 집중하고, 데이터를 어떻게 변화시킬 지 map, filter, aggregation등에 집중한다.

빅데이터 분석에 반드시 필요한 데이터 수집부, 전처리부에서도 이러한 개념이 많이 필요하고, 시스템 자원의 효율화를 이끌어내기 위해서도 이러한 개념이 필요하여 최근 비슷한 컨셉이 각광받는 다고 생각한다. 

<그림-3>

이 자료를 보면 중간에 패러다임의 변화에 대해서 설명이 잘 되어 있다.(https://www.slideshare.net/jongwookkim/ndc14-rx-functional-reactive-programming)

위 자료도 몇 년전에 만들어졌고, 본인이 2015년경부터 데이터수집, DataFlow처리를 위해서 많은 OpenSource들(Nifi, Storm, Spark, Kafka, Flume)을 활용해 왔는데 프로그래밍 패러다임이 변하고 있다는 것을 막연히 느낄 수 있었다.

(http://icthuman.tistory.com/entry/Data-%EC%88%98%EC%A7%91-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%82%AC%EB%A1%80%EA%B2%80%ED%86%A0?category=541260)


Reactive와 유사한 흐름으로 Flow-based Programming 패러다임도 있다.


5. Flow-based Programming

위에서 언급했던 Flow-based Programming에 대해서 잠깐 살펴보면 위키피디아에 아래와 같이 정리가 되어있다.

In computer programmingflow-based programming (FBP) is a programming paradigm that defines applications as networks of "black box" processes, which exchange data across predefined connections by message passing, where the connections are specified externally to the processes

간단히 설명하면 어플리케이션을 블랙박스 프로세스들의 네트워크로 정의한다. 각 데이터들은 connection을 통해서 교환되며 블랙박스 프로세스들을 연결한다.

이러한 컨셉을 사용한 오픈소스가 Apache Nifi 이다. 최초 미국NSA에 의해서 개발되었으며 오픈소스로 공개되어 지금은 호튼웍스의 Data Flow 제품에 포함되어 있다.
거의 10년도 넘는기간 동안 NSA에 의해서 만들어졌다. 그만큼 컴포넌트나 지원하는 프로토콜도 많고 안정성,확장성이 뛰어나다. (그런데 오픈된 버전은 뭔가 좀 부족하다. 잘 죽는다..)

<그림-4>

직접 사용하지 않더라도 시간이 된다면 소스를 보고 분석하는 것을 추천한다.

구조가 잘 짜여있고 유연하게 설계가 되어있다.


6. Functional Programming , Immutable

Reactive Programming 을 보면 반드시 나오는 개념이 함수형 프로그래밍이다.

이 개념은 이미 많은 곳에서 사용되고 있다. (Scala, Spark, Akka 등)

그럴 수 밖에 없는 것이 최근 함수형 프로그래밍의 선두주자가 Scala인데 Spark, Akka, Kafka 등 왠만큼 병렬처리로 유명한 오픈소스들은 Scala로 개발되어 있기 때문이다.


함수형 프로그래밍은 결국 우리가 수학시간에 배웠던 함수를 프로그래밍으로 만드는 개념이다.

y=f(x) 

즉, 어떠한 입력에 대해서 내부계산에 의한 출력값을 주는 함수이다. 숨겨져있는 입/출력이 없고, 상태를 가지고 있지 않다. 

이러한 특징이 병렬처리에서 큰 장점을 가져온다.


- 함수내에서 입력을 변화시키는 것이 원칙적으로 불가능하기 때문에 immutable이 보장

- 매번 같은 수행결과를 보장할 수 있고 

- 복구/재처리가 쉽게 가능하다. 이는 원본을 수정하지 않기 때문인데 Spark를 사용해보면 filter, map등의 메서드등을 호출해도 원본에는 변화가 없으며 새로운 결과객체를 생성하여 돌려준다.

- Akka actor도 이와 유사한 개념으로 받아온 메세지에 수정을 가할 수 없으며, 다른 actor에게 메세지를 보낼 때는 새로운 메세지를 생성한다. 즉 immutable 하다.

- Scala에서 var 와 val의 차이를 찾아보면 좀 더 이해가 쉽다.

- Java도 primitive type이 immutable이긴 하지만 reflection 을 사용하면 객체레벨의 변경이 가능하기 때문에 완벽하진 않다.


7. Reactive vs Event Driven

에서 보면 수많은 사람들이 각각의 방법으로 설명을 하고 있다.

현재까지 이해한바로는 결국 여기에도 개념과 용어의 혼재가 있는 것으로 보인다.
Reactive programming 은 프로그래밍의 하나의 패러다임으로 보는 것이 바람직하고
Event Driven은 시스템을 구현하는 방법, 아키텍처에 가까워 보인다. (Event Driven Architecture라고 많이 이야기한다.)

Event도 결국 DataStream의 일종으로 처리한다면 비슷한 개념으로 볼 수 있으며 Reactive에서는 특정 구성요소등의 언급이 없는 것으로 볼때 맞지 않나 싶다.. (혼자만의 생각?)


참조


<개요>

 - Apache NiFi를 사용하다 보면 디스크 용량을 많이 차지하는 것을 볼 수 있다.

 - 이유는 DataFlow상에서 각 Processor를 거칠때 마다 모든 내용을 다 저장하기 때문이다.

 - 이러한 내용들도 결국 어딘가에 다 저장될 텐데 NiFi에서는 Repositories 라는 논리적 개념을 통해서 이를 정리하고 있다.

 

<내용>

- The FlowFile Repository : 현재 흐름상에 있는 FlowFiles들의 Metadata를 저장한다.

- The Content Repository : 현재 흐름상에 있는 Contents와 과거 FlowFiles를 저장한다.

- The Provenance Repository : FlowFiles들의 history를 저장한다.

이러한 각 Repository들을 통해서 Nifi가 어떻게 Data Flow를 처리하고, 각 Transcation들을 보장하며 메모리와 디스크를 어떻게 사용하고 Log를 관리하고 활용하는지를 더 자세하게 살펴볼 예정이다. 

 

1. FlowFile Repository

2. Content Repository

3. Provenance Repository

 

 

<참조사이트>

https://nifi.apache.org/docs/nifi-docs/html/nifi-in-depth.html#repositories


<개요>

- Apache NiFi의 경우 Flow-based programming 의 패러다임을 잘 살려서 만든 Data Flow를 위한 OpenSource 이다

- FBP는 결국 'Data Factory'라는 컨셉을 Application을 가져가는 것인데 최근에 MS Azure에서 Data 수집으로 제공하는 솔루션이 'Data Factory' 로 출시되어서 그 연관성을 보여주고 있다.

- FBP에서 간단히 정리하자면 블랙박스로 구성된 네트워크의 개념으로 Application을 정의하는 것으로 Processor -<connection>- Processor 의 형태로 메시지를 전달하면서 데이터를 처리하게 된다.

- Apache Nifi의 개념, 기술요소에 대해서는 나중에 정리하도록 해야겠다.

 

<내용>

- NiFi의 경우 이렇게 데이터의 흐름에 초점을 맞추고 Application을 개발하며 이 때 처리되는 내용들을 여러 Repositories로 저장한다.

- FlowFiles가 가장 중요한 개념인데, 하나의 FlowFile은 하나의 Record를 가리킨다. 이 때 content에 직접 접근하는 것이 아니라 Pointer로 관리하고 그외 content의 속성을 나타내는 attributes와 events  도 같이 포함된다. 즉, (Pointer + Attributes + Events)로 이해하는 것이 쉽다.

 각 Attribute들은 key / value쌍으로 메타정보를 담고 있다. (ex, filename등 processor별로 다를 수 있다.)

- 동시성 프로그래밍에서 필수로 꼽히는 요소가 immutable한가 인데,  Spark RDD , Akka Actor들이 그렇듯이 NiFi에서는 이러한 정보들을 Repositories에 저장할때 immutable하도록 관리한다.

- 예를 들어서 FlowFile의 attribute에 변경이 일어날 경우, 기존 내용을 수정하는 것이 아니라 새로 복사본을 만들어낸 뒤에 저장한다.

- Content에 변경역시 기존의 Content를 읽어서 새롭게 기록하고 FlowFile의 pointer를 새로운 위치로 업데이트 한다.

- 이를 통해서 OS Caching을 활용하고 Randon read/write의 비율을 감소시키는 이점이 있는데 이는 Kafka가 사용하는 방식과 매우 유사하다.

 

<Copy on write의 개념>

- Copy on write는 implicit sharing 또는 shadowing이라고 불리는 기법이다.

- 같은 Resource에 대해서 수정사항이 없지만 복제가 필요한 상황에서 사용된다.

- 꼭 새로운 복제본을 만들 필요는 없지만 수정사항이 발생한다면 복제본을 반드시 만들어야 한다.

- 활용예제

 a. 여러 프로그래밍 언어에서 문자연산 (ex, "+" )을 할 때 많이 사용된다.

 b. snapshot을 생성하는 것에도 사용된다. (Redirect on write, Copy on write)

 

<그림-1>

 

<Nifi에서의 copy on write활용>

- CompressContent processor , Merge processor 등 여러 프로세서에서 활용하고 있는데 이전 프로젝트에서 많이 사용했던 Merge의 예를 살펴보면

- Merge의 대상이 되는 원 프로세서에서 각 FlowFile이 넘어오면 MergeContent 프로세서에서는 모든 FlowFile을 하나로 합쳐서 새로운 하나의 FlowFile을 생성한다.

- 예를 들어서 10개의 프로세서가 하나의 MergeContent 프로세서로 연결되면 10 FlowFiles => 1 FlowFiles로 변경이 된다. 이 과정에서 Interval등을 조정하면 더 많은 FlowFiles을 하나로 합칠 수도 있으나 connection에 담기는 건수가 너무 클 경우 메모리 오류등이 발생할 수 있으니 주의해야 한다.

- MergeContent프로세서 역시 입력된 FlowFiles을 수정하는 것이 아니라 새로운 FlowFile을 생성하고 해당 Location을 지정한다.

 

<정리>

- 원본의 변화가 없기 때문에 에러가 발생할 경우 재현이 가능하다.

- 각 단계별로 별도 저장을 하기 때문에 저장소 공간 확보를 잘 해줘야 한다.

- Memory를 사용하여 처리하는 구조이기 때문에 처리하는 데이터의 양, 주기등을 고려해야 한다.

- 실제로 프로젝트에서 Merge단계에 Memory오류가 자주 발생하였다.

 

<참조사이트>

https://en.wikipedia.org/wiki/Flow-based_programming

https://en.wikipedia.org/wiki/Copy-on-write

http://storagegaga.com/tag/copy-on-write/

https://nifi.apache.org/docs/nifi-docs/html/nifi-in-depth.html#copy-on-write

 

 

데이터의 유형

데이터를 수집함에 있어서 크게 두 분류로 나눌 수 있다.

Event기반으로 계속 흘러들어오는 데이터와 기존 시스템에 이미 적재되어 있는 데이터.


데이터의 활용

 흔히 말하는 빅데이터 분석은 실시간 데이터를 처리하는 것이 아니라 HDFS상에 적재되어 있는 데이터를 기반으로 Map/Reduce등의 병렬프로그래밍을 통하는 것이었으며 최근에는 실시간 분석에 대한 요구도 높아져서 이를 위한 오픈소스들도 많이 생겨나고 있다.

 기존에는 적재되어 있는 데이터를 기반으로 분석하는 것과 실시간 Event가 분리되어 왔다면

앞으로는 두 종류의 데이터가 하나로 합쳐져서 기존 데이터를 기반으로 모델을 구축한 뒤에, 실시간 데이터들을 모델에 적용하여 예측,분류,Anormaly Detection 등을 수행하게 된다.

 람다아키텍처에 대해서는 별도로 정리하도록 해야겠다.


데이터의 수집방법

 모델구축을 위해서는 대량의 데이터가 반드시 필요하여 이를 위해 대표적인 수집을 위한 오픈소스 툴로 Flume, Sqoop, Gobblin등이 있다. 현재 본인은 File,RDB의 데이터를 수집하기 위해서는 Gobblin을 사용하고, 그 외의 Event기반 데이터는 Flume을 사용하여 수집하고 있으며 메시지 유실, 분산처리 scale out을 위해서 Kafka를 사용하고 있다. 그리고 이러한 작업들을 스케쥴링하기 위해서 Oozie를 사용하고 있다.

 Hadoop Ecosystem에 따른 거의 보편적인 아키텍처라고 볼 수 있겠지만 실제로 사용해보면 많은 어려움이 있다. Cluster구조를 가져간다는 이야기는 결국 네트워크에 의한 멤버쉽 관리가 되어야 하며, 병렬처리를 위해서는 처리량의 분배가 균일하게 이루어져야 한다는 것이다. 테스트를 통해서 적절한 수치를 찾는 것도 쉽지 않은 일이며, 막상 결과가 나왔을때 설명가능한 이유를 찾지 못하는 경우도 있다;;

 그리고 수많은 오픈소스로 이루어져있기 때문에 개별 설정도 어렵고, 서로 연관관계 있는 오픈소스들의 설정이 꼬여있을때는 오류발생의 주 원인이 되며 관리하기도 매우 어렵다.



이미지 출처 : http://7246-presscdn-0-21.pagely.netdna-cdn.com/wp-content/uploads/2015/02/Apache-Hadoop-Ecosystem.jpg


사례참고 

 해당 내용에 대한 아키텍처 공유사례는 많지만 구체적인 수치와 유형별 장단점에 대한 내용은 찾기가 어렵다.

실제로 같은 구성요소를 사용하더라도 그 모습은 요구사항에 따라서 천차만별로 구현된다.

최근 cloudera에 공유된 좋은 사례가 있어서 살펴보았다. 

(http://blog.cloudera.com/blog/2016/03/building-benchmarking-and-tuning-syslog-ingest-architecture-at-vodafone-uk/)


0. Overview

syslog를 수집함에 있어서 다음과 같은 high-level의 아키텍처가 있다.

Flume, Kafka 모두 event기반으로 단위 건당 수바이트의 자료를 처리하기에 적합한 오픈소스이며 내부구조에 대해서는 공식사이트를 참고하는 것이 가장 좋다.



1. 구성요소

Flume

Source-Channel-Sink의 구조로 이루어져있다. 이미 다양한 유형의 데이터에 대해서 Source, Sink등이 구현되어 있어서 쉽게 가져다 사용할 수 있으며, 필요한 부분은 사용자 구미에 맞게 코딩할 수 있다.

해당 사례에서는 syslog source - memory channel - Kafka sink와 Kafka source - memory channel - HDFS sink 를 동시에 사용하고 있다. (Consolidation - https://flume.apache.org/FlumeUserGuide.html)


Kafka

Kafka는 pub/sub구조의 message queue로 볼 수 있는데, 저장소로 Disk공간을 쓰는 것이 특징이다. 따라서 메시지 유실이 되지 않는 것을 최우선으로 하는 시스템에서 사용한다. (그런데 메시지 유실이 되어도 됩니다 라는 시스템은 한번도 본적이 없다.;)


2. 검토요소

성능향상을 위해서는 다음과 같은 요소들을 검토할 필요가 있다.

  • Number of collection tier agents and nodes
  • Number of Kafka sinks per collector tier agent
  • Number of Kafka brokers
  • Number of Kafka topic partitions
  • Kafka broker interconnect network
  • Number of Kafka sources
  • Number of edge tier nodes and Flume Agents per node
  • Number of HDFS sinks
  • Batch-sizes on all of the sinks and sources

사이트에 자세한 내용들이 나와있으니 그냥 간단히 정리해보았다.


* Kafka

- partition의 수는 클러스터상의 디스크수와 최소한 같아야 한다.

- Kafka는 Broker를 통해서 모든 작업을 처리하기 때문에 Broker의 숫자와 메모리를 충분히 확보해주어야 한다. 

- Flume Sink(Kafka Producers)는 일반적으로 Cluster내의 하나의 Partition에 대해서 작업을 하기 때문에 성능을 확보하기 위해서는 충분히 많은 Sink를 갖는게 좋다. 그래야 Kafka Broker가 병렬처리가 원할해 진다.

Birthday problem과 같은 문제로 한 Partition에 2개이상 Sink가 접근할 가능성이 있다! write가 균등하게 되면 좋다.



- Flume Source(Kafka Consumers) : Producer나 Partition에 수에 명확한 관계는 없다. Consumer의 수가 Partition의 수보다 많을 경우 idle상태가 발생한다. Consumer의 수를 Partition보다 작게 하면 모든 Consumer가 일을 하게 된다. 



4개의 Sink가 하나의 Partition을 남겨두고 일을 해서 idle이 발생하고 Kafka Source가 놀고 있는 상황



* Flume - Kafka

Flume과 Kafka를 연계하여 사용할 수 있는 방식은 크게 두 가지가 있다.

1. Kafka Sink를 통해서 기록하고 Kafka Source를 통해서 읽어가는 경우

2. Flume Channel을 Kafka Channel로 사용하는 경우 (Channel의 type은 이외에도 memory, file 등이 있다.)

해당 사례에서는 Kafka channel의 throughput문제와 balanced configuration이 어려운 점을 주요 문제로 생각해서

Kafka Source , Sink방식을 채택했다고 한다. (memory channel의 유실 가능성을 염두하더라도..)



현재 본인은 2번 방식을 사용하고 있었는데. (일단 사용하기가 편하다. 세팅도 간단하고..) 

사용량이 늘어날 것을 감안하면 1번 방식으로 바꿔야 할듯 하다. 

1번도 그렇게 어렵지는 않다. Kafka Source나 Sink는 이미 있다.


3. 결론

Flume-Kafka를 적절하게 사용하기 위해서는 세팅이 필요하다는 내용을 다음과 같이 정리하고 있다.

  • Enough HDFS sinks to handle the output from the Kafka tier
  • A number of Kafka sources fewer than or equal to the number of partitions
  • Sufficient number of partitions that the sources are not the bottleneck, and also that all disks in the Kafka cluster are utilized
  • Enough Kafka sinks such that we have a good probability of not leaving one or more partitions, and hence sources, idle


향후 해야할 일

 빅데이터에 관련된 구성요소들은 거의 대부분 오픈소스이다. 그리고 많은 사람들이 오픈소스는 공짜이며 가져다 쓰기만 하면 된다고 한다. 그럼 빅데이터는 쉽겠네?? 아무나 다하겠네..

반은 맞고 반은 틀리다. 아무나 가져다가 편하게 쓸 수는 있지만...

 OS에 대한 기본적인 이해, Java기반의 어플리케이션 개발 Maven/Gradle기반의 CTIP환경, 요구사항에 맞는 아키텍처 설계 및 오픈소스 선정/커스터마이징 , 추후 버전변경에 따른 릴리즈 관리, 기존 레가시 시스템과의 연계등등 해야할 일도 많고, 기본적으로 소프트웨어관련 일정수준의 지식이 없이는 불가능하다.

(오픈소스를 한번이라도 열어보신분 들은 아시겠지만 정말 깔끔하다. OOP 5대원칙을 이해못하면 예쁘게 커스터마이징하기도 쉽지 않다. 거기다가 데이터분석, 머신러닝 등은 확률,통계,선형대수학 등등 우리를 괴롭혔던 공대수학이 최대의 적으로 등장한다. ㅋㅋ)


항상 공부해야 한다. 끝!



 소프트웨어들의 완성된 모습을 보면 그 소프트웨어를 만든 조직의 모습과 놀랄만큼 닮아있는 것을 보게 된다. 콘웨이법칙이라고 해서 나름 유명한 법칙이다.

 이는 단순히 모듈의 갯수나 레이어등을 말하는 것 뿐만 아니라 Responsibility나 Collaboration 방식까지도 매우 유사하게 된다.


 최근에 많은 기업들이 우수한 소프트웨어를 만들기 위해서 해외의 유명한 전문가들도 모셔오고, 강의도 듣고 BP를 찾아서 적용하기 위해서 노력한다. 그런데 가장 중요한 것은 문화이다.


 Top-Down방식으로 경영전략을 세우고, 조직별로 목표를 수립하고 KPI를 달성하기 위해서 일하면, 그러한 모습의 소프트웨어가 나온다. 정해진 프로세스를 준수했기 때문에 정해진 결과물이 나온다. 때로는 수준에 못 미치더라도 억지로 끌어올린다. 품질지표를 만족하는 결과물이 어떻게든 나온다.

그러나 그 소프트웨어의 수명은 조직의 수명과 동일하기 때문에 몇년뒤면 자연스럽게 사라지고 다시 다른 소프트웨어를 만들기 시작한다. 개발자는 그때 그때 필요한 부품에 불과하다.

이렇게 해서는 절대 좋은 소프트웨어가 만들어질 수 없다.


 하나의 소프트웨어 프로덕트가 어느정도의 안정성과 성능, 기능을 확보하려면 제품마다 다르겠지만 최소한 2년, 넉넉하게는 3~5년정도가 걸린다. 그리고 이러한 성장가능한 구조, 아키텍처를 세우는 것은 무엇보다도 중요하다.

 가장 피해야 할 시나리오는 다음과 같다. 최초 A사이트를 타겟으로 v 1.0 을 만들었다. 이후 B사이트가 새로 생겼다. 그런데 새로운 요건을 만족시키기 위해서는 v 1.0 로는 안되기 때문에 v 2.0을 만든다. 물론 v 1.0과는 호환이 안된다. C사이트가 새로 생겼다. 다시 v 3.0을 만든다. 버전별 유지보수는 시간이 흐를수록 불가능이 된다..


 그렇다고 좋은 아키텍처를 잡기위해서 초반에 마냥 시간을 보낼 수도 없다. 우리가 선정한 기술이 목표와 부합하는지 검증도 해야하고, MVP모델도 개발해야한다. 실제 적용도 빨리 해봐야 더 많은 요구사항을 뽑아낼 수 있다. 이러한 과정을 진행하기 위해서 우리는 시간이 매우 부족하기 때문에 애자일을 선호하게 된다.


 많은 사람들이 착각하는 것이 애자일을 하나의 방법론으로 이해하고, 과거해왔던 방식처럼 무언가를 자꾸 적용하고 표준 프로세스와 산출물을 만들려고 한다.

착각하면 안된다. 애자일의 핵심은 좋은 코드를 빠르게 만들어 내는 것에 중점을 두고, 이를 방해하는 것들을 없애는 것이다. 필요한 것을 넣는 것이 아니라 필요없는 것을 빼는 것이다. 꼭 필요한 것만 남기는 것이다.

애자일을 설명할 때 나오는 수많은 기법들이 있다.

이건 그냥  "우리가 이렇게 해봤더니 도움이 되더라.."  일뿐이다. 참고자료이지 정답은 아니다.

애자일 선언과 그 배경원칙들에 대해서 수십번 읽어보시고 곱씹어보시기를 추천드린다.


 기존에 개발팀이 개발하던 방식을 존중하고, 그들의 문화를 관찰해야 한다. 개발자 한명 한명의 성향까지도 고려해야 한다. 어느조직에나 딱 맞는 방법론이란 존재하지 않는다. 자신들만이 가지고 있는 방법을 더욱더 간결하게 다듬어서 맞춰가는 것이 애자일이다.


 실질적으로 이렇게 일하는 문화가 자리잡히면 자연스럽게 Bottom-Up으로 할 일들이 도출된다. 내가 이렇게 만들었더니 이러한 단점이 있더라. 이걸 어떻게 보완하면 좋을까? 서로 의견을 나누어서 더 나은방향으로 개선시킨다. 

 자신의 설계를 공유하고, 코드를 공유하며 Review한다. 자연스럽게 소프트웨어의 품질을 올라갈 수 밖에 없다.

 남들에게 뒤쳐지지 않기 위해서 스스로 학습하고 새로운 것을 항상 찾아본다. 냅두면 알아서 공부하고 자기가 만든 분신과도 같은 소프트웨어를 더욱 발전시키기 위해서 노력한다. 자기주도 학습이다.(self-self-self!!!)

다른사람들과 개발순서를 맞춰야 하기 때문에 자연스럽게 일정이 조정되고, 그 일정은 매우 타이트하다.

 스스로 세운 일정을 준수하는 것은 본인과의 약속이기 때문에 누가 시키지 않더라도 야근을 한다. 그리고 만족할만한 성과가 나온 날은 기분좋게 일찍 퇴근한다. 

다른 사람들은 속일 수 있을지 몰라도 나 자신은 속일 수 없다.

 개발자의 역량이 발전하고 다시 소프트웨어는 새로운 기능으로 무장한다.(개발자에게 있어서 소스는 분신과 같은 존재이다.)


왜 조직은 개인을 믿지 못하고 자꾸 관리하려 드는가? 이에 대해서는 각자 반성해볼 필요도 있다.

그동안 개인은 어떻게 일해왔길래 조직이 신뢰할 수 없는 것일까...


좋은 소프트웨어는 관리를 통해서 만들어질 수 없다.

좋은 아키텍처는 좋은 조직과 문화에서 만들어진다.


조직과 문화는 사람이 만들고 구성원은 계속 변화한다.

'Software Architecture' 카테고리의 다른 글

Apache NiFi In Depth #2 (Copy on write)  (0) 2017.11.02
Data 수집 아키텍처  (0) 2016.03.13
Communication_Offerings  (0) 2015.03.25
Go Reactive Systems  (0) 2015.03.25
scale out 이 가능한 architecture? micro service?  (0) 2015.03.25

akka를 보던 중 at most once 전달이기 때문에 메시지의 유실이 발생할 수 있다는 내용이 보인다.

(http://doc.akka.io/docs/akka/snapshot/general/message-delivery-reliability.html)


그렇다면 akka를 못 쓰는 것인가? 보완하도록 메커니즘을 구성해야 되겠네....

생각난김에 메시지전송 패턴에 대해서 간략히 정리를 해본다. 


  • at-most-once delivery means that for each message handed to the mechanism, that message is delivered zero or one times; in more casual terms it means that messages may be lost.
  • at-least-once delivery means that for each message handed to the mechanism potentially multiple attempts are made at delivering it, such that at least one succeeds; again, in more casual terms this means that messages may be duplicated but not lost.
  • exactly-once delivery means that for each message handed to the mechanism exactly one delivery is made to the recipient; the message can neither be lost nor duplicated.


1. At least once

 실패가 일어나더라도 메시지의 유실이 발생해서는 안되고 복구에 너무 오랜시간이 걸려도 안된다.

메시지는 최소한 한번 이상 전달되어야 한다

<context>

메시지의 전달을 확실히 보장하는 것이 최우선이며, 중복이 발생해도 무방하다.

<solution>

각 메시지를 수신했을 때 ack응답을 되돌려주도록 한다. 만약에 ack를 정해진 시간내에 못 받을경우 다시 보낸다.


2. At most once

 메시지는 많아도 한번 전달된다.

 <context>

 중복없이 빠르게 처리하는 것이 목표이다. 유실되어도 critical하지 않은 경우

 <solution>

 전송에 관련된 상태등을 기억하지 않는다. fire-and-forget


3. Exactly one

 메시지는 정확하게 한 번만 전달되어야 한다. 금융권이나 기타 트랜잭션을 반드시 보장해야하는 경우다. 중복x!

 <context>

 분산처리 환경에서 매우 중요한 요건으로 메시지 유실이 발생해서도 안되고, 중복이 발생해도 안된다.

 <solution>

 메시지를 생성할 때 unique message identifier를 붙인다. 이 identifier를 이용해서 송신자와 수신자간에서 발생한 중복메시지를 filter처리한다.


http://www.cloudcomputingpatterns.org/

 최근 몇 년전만 해도 응답시간 수초, 수십개의 서버, 기가단위의 데이터로 시스템이 구성되었지만 오늘날에는 모바일, 수천대의 클라우드 기반환경으로 변화하면 millisecond단위의 응답시간, 페타바이트 데이터 등의 요건으로 인해서 과거의 소프트웨어 아키텍처로는 이를 수용하기가 어려운 상태이다.

 결국 우리는 Responsive, Resilient, Elastic and Message Driven 한 시스템을 원하고 이것을  Reactive Systems이라고 부른다. ( 보다 유연하고 scalabe하면서 장애도 방지하는 그런 시스템)

Reactive Systems are:

Responsive: The system responds in a timely manner if at all possible. Responsiveness is the cornerstone of usability and utility, but more than that, responsiveness means that problems may be detected quickly and dealt with effectively. Responsive systems focus on providing rapid and consistent response times, establishing reliable upper bounds so they deliver a consistent quality of service. This consistent behaviour in turn simplifies error handling, builds end user confidence, and encourages further interaction.

Resilient: The system stays responsive in the face of failure. This applies not only to highly-available, mission critical systems — any system that is not resilient will be unresponsive after a failure. Resilience is achieved by replication, containment, isolation and delegation. Failures are contained within each component, isolating components from each other and thereby ensuring that parts of the system can fail and recover without compromising the system as a whole. Recovery of each component is delegated to another (external) component and high-availability is ensured by replication where necessary. The client of a component is not burdened with handling its failures.

Elastic: The system stays responsive under varying workload. Reactive Systems can react to changes in the input rate by increasing or decreasing the resources allocated to service these inputs. This implies designs that have no contention points or central bottlenecks, resulting in the ability to shard or replicate components and distribute inputs among them. Reactive Systems support predictive, as well as Reactive, scaling algorithms by providing relevant live performance measures. They achieve elasticity in a cost-effective way on commodity hardware and software platforms.

Message Driven: Reactive Systems rely on asynchronous message-passing to establish a boundary between components that ensures loose coupling, isolation, location transparency, and provides the means to delegate errors as messages. Employing explicit message-passing enables load management, elasticity, and flow control by shaping and monitoring the message queues in the system and applying back-pressure when necessary. Location transparent messaging as a means of communication makes it possible for the management of failure to work with the same constructs and semantics across a cluster or within a single host. Non-blocking communication allows recipients to only consume resources while active, leading to less system overhead.

Large systems are composed of smaller ones and therefore depend on the Reactive properties of their constituents. This means that Reactive Systems apply design principles so these properties apply at all levels of scale, making them composable. The largest systems in the world rely upon architectures based on these properties and serve the needs of billions of people daily. It is time to apply these design principles consciously from the start instead of rediscovering them each time.

참고)http://www.reactivemanifesto.org/

===============================================================================================================

슬라이드쉐어에서도 공유가 되고 있으니 참고할 수 있다.

http://www.slideshare.net/mircodotta/go-reactive-eventdriven-scalable-resilient-responsive-systems?qid=bc7cfcab-8c5b-4d37-a122-11590578803f&v=qf1&b=&from_search=11

===============================================================================================================

 디스크, 메모리, 서버등의 자원은 갈수록 저렴해지고 일반적이 되어가고 있으며 동시사용자는 기하급수적으로 늘어났다.

과거 중요한 서비스는 반드시 죽지 않아야 하고 고정되어 requset/response를 보장한다는 개념에서,

이제는 수백,수천개를 띄워놓고 사용자 수에 따라서 서비스량을 조정하면서도 약간의 문제는 상관하지 않고 돌아간다는 개념으로 변화하고 있다.

 

 

패러다임 변화에 따라서 어플리케이션도 변경이 되어야 한다는 내용.

이러한 내용을 반영하여 나온 것이 reactive programming으로 현재 typesafe에서 주도하고 있다.

akka,scala를 통해서 이를 구현하고 있으며 많은 IT서비스기업에서 사용하고 있다.

+ Recent posts