RedHat?

RedHat은 오픈소스 진영에서 가장 오래된 기업 중 하나입니다. 사업구조는 라이선스를 받는 것이 아니라 교육프로그램, 기술지원, 컨설팅으로 수입을 창출합니다. (기업용 소프트웨어를 판매하기도 합니다!)

사용하는 제품들은 대부분 오픈소스로 구성되어 있습니다. 주로 오픈소스 기반으로 공유하면서 개발을 진행하고 중요한 개선사항들이 발생하면 이를 기업용에 반영하는 사이클을 가져갑니다.

최근 IBM이 RedHat을 인수했으나 당분간 변화없이 갈 계획이라고 합니다.

OpenSource

오픈소스는 소스가 공개되어 있어 누구나 가져가서 사용할 수 있지만 무료를 의미하는 것은 아니니 오해하면 안됩니다. 어떻게 설정하고 어떻게 사용하느냐에 따라서 성능이 천차만별이고, 제품의 발전속도가 빠르다는 것이 특징입니다. 결국 잘 사용하기 위해서는 기술적 이해도와 개발역량등이 매우 중요합니다. 현재 시장에서 사용되는 기술스택은 거의 차이가 없습니다. 중요한 것은 KnowHow이고 RedHat은 여기에 초점을 맞추고 있습니다.

(간단히 사용하는 것은 쉬우나, 마스터하는 것은 어렵다. Like 게임)


 

개인적으로 Red Hat의 방향성이 참 마음에 든다. 오픈소스 시장에 딱 맞는 모델이 아닐지.. win win전략


-오전 세션

<Business Keynote: Digital Transformation & the Open Organization TBA>

새로 출현하는 모든 것들은 기존의 것들에 많은 영향을 주는데 Digital Transformation도 그러합니다. 조직, 문화, 도구 많은 것들이 변화하며, 모든 산업 영역이 도전을 받고 있습니다. 롯데카드 김창권 CEO가 나와서 간단히 사례를 공유하였습니다. 모든 것이 변화해야 Digital Transformation을 추진할 수 있습니다. 특히 Hybrid Cloud가 자주 언급되었는데 뒤에서 설명합니다.

 

<Simple Path To Secure and Automated Multicloud>

RedHat의 파트너인 JUNIPER 사가 현황을 공유하였습니다. JUNIPERSDN을 비롯한 Network분야를 전문으로 하며 멀티클라우드를 운영하는 것에 대해서 간단히 소개하였습니다.


자동화의 영역이 늘어난다는 것은 결국 Software Define xxx가 늘어나는 것을 의미합니다. (Network, DataCenter, Deployment ..)



 

<Technical Keynote : BigPharm Morphs into a Digitally Transformed Business>

BIG PHARM BIONODE 라는 두 회사가 하나로 합쳐지는 과정을 시뮬레이션하면서 데모를 하는 시간이었습니다. 두 개의 회사가 어떻게 다른 회사의 데이터모델을 공유하고 이를 어플리케이션에 반영하는지를 간단히 데모를 통해서 보여주었습니다.



 다른 도메인의 모델과 프로세스를 어떻게 엮을 것인지가 핵심 포인트며 이 부분을 코딩이 아닌 UI를 통해서 쉽게 할 수 있도록 하는 것이 목표라고 하였습니다. (정말?? IT업계의 마르지 않는 떡밥느낌이다..)

 

주로 간단한 예를 중심으로 데모가 진행되었기 때문에 아직 실제 비즈니스와는 Gap이 있다고보입니다

 

<Using Innovation to Drive Business Transformation>

Open Organization이야기가 다시 나옵니다. 전통적인 조직구조에서는 상위의사결정자가 무엇(WHAT)을 할지 결정하고, 중간관리자가 어떻게(HOW) 할지를 고민하면, 구성원은 이걸 왜(WHY) 해야하는지 고민했습니다.

Open Organization에서는 구성원 스스로 일의 목적, 이유(WHY)를 찾습니다. 스스로 동기부여를 합니다. 중간관리자는 어떻게(HOW) 하면 일이 되게 만들지를 고민하며, 상위의사결정자는 전체적인 방향성(WHAT)을 설정합니다. 조직의 모습과 문화와 일하는 방식이 바뀌어야 합니다.


Open Source가 발전하게 된 것도 비슷한 맥락입니다. 사용하는 사람들, 만드는 사람들이 일을 찾아내기 때문에 빠르고 동기부여가 됩니다. 모든 것이 투명하게 관리되고 문제를 숨기는 것이 아니라 공유하고 빠르게 해결하는데 초점을 맞춥니다. 필요에 따라 개발됩니다.

Agile, DevOps등도 결국 일하는 방식의 변화하는 바가 가장 큽니다.

 

<Session A-1 Red Hat과 함께하는 하이브리드 클라우드 구축 방안>

멀티클라우드는 단순히 여러 종류의 클라우드를 사용하는 것이지만 하이브리드 클라우드는 이러한 여러 종류의 클라우드를 하나의 기술(OpenStack)로 관리하는 것이 목표입니다나아가서는 기존 Legacy까지!

물론 아직 갈길이 멉니다. (ex, 리전이 다를경우 Storage공유가 불가능함)

 일반적으로 사람들이 혼돈하는 것은 클라우드에서 중요한 것은 인프라라고 생각하는 것입니다.

중요한 것은 Application입니다. ApplicationScale Out가능하고 fault tolerance하게 개발되어야 하는 것이 먼저입니다. Application이 그렇게 개발되어 있다면 BareMetal, PrivateCloud, PublicCloud는 사실 크게 의미가 없습니다. 기존에도 MicroService는 있었습니다. 인프라는 이것을 기능적으로 더 편리하게 해주는 것입니다. 말 그대로 Cloud(구름)이기 때문에 DB, Network가 어떻게 구성되어 있는지 신경을 쓸 필요가 없게 되는 것입니다.

이제는 모든 것이 Software Define으로 가능합니다. “모든 기업은 소프트웨어 기업이다라는 말처럼 상품/비즈니스/개발/배포/인프라 모든 것이 소프트웨어를 통해 자동화가 됩니다. 적절한 API를 잘 사용하는 사람이 중요합니다.

 

<Session B-2 클라우드 네이티브 애플리케이션 개발을 위한 8가지 단계>

기술변화에 대한 배경이해가 필요합니다. 새로운 것을 개발하는 측면과 기존 것을 운영하는 측면의 대립을 의미하는 것이 아니라 보다 빠르게 개발하고 빠르게 배포하는 것이 궁극적인 목표입니다.

 

Service-based, API-Driven, Containers, DevOps가 일반적인 클라우드 네이티브App의 요소로 볼 수 있습니다.

결국 시장이 빠르게 변하고 있기 때문에 그에 맞춰서 개발도 빠르게, 변경/배포도 빠르게 되어야 합니다.


8단계를 통한 설명 (8단계는 선택적으로 가능함!)

1단계 : 대부분의 큰 조직들이 우리는 DevOps를 적용했다고 하지만 무엇을 하고 있는지에 대해서는 명확히 말하지 못합니다.


특히 사업,App,Infra등에서 실패를 겪는 것에 거부감을 가지면 안됩니다. 빠르게 극복하는 것에 초점을 두어야 합니다. 아무도 사용해본 적이 없고, 만들어 본적이 없기 때문에 당연히 실패합니다.  (ex, Google BigTable, Amazon OpenStack, )

2단계 : 기존 구조를 개선합니다. API기반으로 변경해나가고 Container형으로 만들어갑니다.

3단계 : 클라우드를 위한 서비스를 사용합니다.

4단계 : 필요에 따라 가장 적합한 기술을 선택합니다. (장표의 툴은 의미가 다릅니다.)

5단계 : 개발자가 필요에 따라서 인프라를 선택하고 사용합니다.

6단계 : 배포를 위한 내용들을 자동화합니다.

7단계 : CI/CD PipeLine을 구성합니다. 단순배포 뿐만 아니라 변경분만 배포, 실패시 Rollback, UnitTest등의 액션들을 자동화합니다.

8단계 : 부분적으로 점진적으로 Service형태의 아키텍처로 만듭니다. (과거 SOA, Web, 의 연장, 내외부 포함)


<Session B-3 API 중심의 애자일한 통합 방법 : API에서부터 iPaaS까지>

과거의 개발방식과 현재의 개발방식에는 많은 차이가 있습니다. 이를 어떻게 통합해 나갈 것인가.?

 

Hybrid Cloud Public Cloud, Private Cloud뿐만 아니라 과거의 시스템도 하나로 통합해야 한다고 봅니다.  (서비스, 어플리케이션, API, 데이터) 관점


주의해야 할 점은 Centralize가 아닌 필요한 곳만 통합하는 것이 되어야 한다는 점입니다.

(RedHat API, Disributed Integraion, Container 관점으로 접근


<Session B-5 Red Hat solutions on Azure와 함께하는 Empowering Digital Transformation>


기조 연설에 나왔던 내용입니다. 증기기관의 발명이 모든 것을 바꾸었고, 전기의 발명이 그러했듯Digital Transformation이 모든 기존의 방법을 바꾸고 있습니다.


오픈소스에 대한 마이크로 소프트의 접근도 바뀌어 가고 있습니다.

과거 Vendor사에 의존적이었던 것들이 이제 오픈소스로 누구나 접근할 수 있으며 필요에 의해 만들고 빠르게 공유하며 발전하는 세상이 되었습니다.


Red HatMicrosoft도 같이 일합니다!

Linux 든 Windonws 든 RedHat Midlleware구입하면 모두 지원한다고 함!)

심지어 OpenJDK도 OS관계없이 지원함.


<정리 >

RedHat Forum에서 전반적으로 말하는 것이 시장의 흐름과 크게 다르지 않습니다.

1.     Cloud의 등장이 Legacy의 사라짐을 의미하지는 않습니다. 다만 API를 중심으로 시스템들이 통합되어가는 모습이 될 것입니다.

2.     모든 기업은 소프트웨어 기업이 될것이다. 는 이미 이루어지고 있고..

3.     또한 IT내에서도 하드웨어 역시 소프트웨어로 통합되고 있습니다. (Software Defined Network, Datacenter)

4.     시스템개발 뿐만 아니라 운영/자원관리/배포/인프라의 영역도 개발로 전환되고 있으며 이에 따라 IT전문가의 영역도 개편되고 있습니다

     기존의 SA,TA,DA,DBA의 역할구분도 점점 없어짐. 주변에 보면 부지런한 DA,DBA들은 코딩 배워서 NoSQL병행하신지 오래되었고, SA,TA분들도 클라우드업체의 솔루션 아키텍트로 전환해서 제공되는 구성요소를 활용해서 고객에게 제공하는 (코딩) 영역으로 전환하고 있음.

     거꾸로 개발자들이 Network, DB, OS 공부해서 이제는 클라우드에서 알아서 골라서 설치하고 튜닝해서 사용하고 있음.

     기존의 영역만 고집하는 사람은 시장에서 도태됨.

5.     기술의 발전 흐름은 결국 시장의 변화에 대응하기 위함입 

     빠르게 개발하여 시장에 적기에 출시하는 것이 Agile이고 

      그 피드백을 반영하여 다시 제품에 녹이는 것이 DevOps

      CI/CD는 이러한 과정에서 수작업을 줄여서 속도를 개선하고 실수를 없애는 것

      사용량에 따라서 유동적으로 변화 가능한 Application구조가 MSA이며

      이를 뒷받침하는 기술이 container, cloud

       각각의 비즈니스 요건에 따라서 다른 기술을 사용하는 것이 polyglot, Service-Based이며

       이러한 시스템을 다시 하나로 합치기 위해서 API-Based가 중요해지고 있음.

6.   Cloud에서 인프라는 큰 의미가 없음. 중요한 것은 Application임. 

     Application이 Scale out, Fault tolerance 하도록 만들어져야 함. 예전에도 이러한 구조의 프로그램들은 있었으나 인프라기술이 뒷받침되지 못해서 수작업으로 진행해야 했던 부분들임

     이제는 Cloud에서 이러한 것들을 편하게 할 수 있도록 기술적으로 지원하고 있음

     말그대로 구름뒤는 신경쓸 필요없이 Application을 유연하게 만드는 것이 집중하면 됨.

     거꾸로 Application이 그렇지 않다면 무슨 인프라를 사용하던 크게 나아지지 않음



 모든 것이 연결된다. 기술이기도 하고 문화이기도 하고 도구 이기도 함!

 이중에 하나라도 결여가 되면 진정할 발전이 어려움

 (다들 잘 아시는 콘웨이의 법칙, 소프트웨어 구조가 소프트웨어를 개발하는 조직의 구조를 따라갑니다.)



'컨퍼런스' 카테고리의 다른 글

Azure Everywhere 2019 후기  (0) 2019.01.12
Spark Summit 2017  (0) 2017.02.16

최근 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

 

 

<개요>

YARN은 현재 CPU 와 Memory에 대해서만 스케쥴링을 수행한다.

따라서 Spark를 YARN Cluster 혹은 Client로 수행할때에도 해당 리소스의 제어를 받게 되는데 이 때 최적의 세팅값을 찾는 법을 확인해본다.


<내용>

<그림 1 : Spark와 Yarn Containter간의 메모리 관계>


A. 위의 그림을 참고로 해서 Spark와 YARN Configuration값을 살펴보면


1) Sparak Executor

--executor-cores 와 spark.executor.cores  는 Spark Executor가 동시에 수행할 수 있는 tasks의 수이다. 


--executor-memory 와 spark.executor.memory 는 Spark Executor의 heap size이다.


세팅된 두 값은 모든 Spark Executor가 같은 값으로 동작한다.


2) num of executors

--num-executors 와 spark.executor.instances  Spark Executor의 수를 의미하지만

spark.dynamicAllocation.enabled 옵션을 통해서 동적으로 제어된다. (CDH 5.4/Spark 1.3)



3) YARN

yarn.nodemanager.resource.memory-mb 은 각 노드에서 컨테이너들이 사용할 수 있는 메모리의 총합이고


yarn.nodemanager.resource.cpu-vcores 은 각 노드에서 컨테이너들이 사용할 수 있는 core의 총합이다.



4) 보정수치

각 컴포넌트의 관계로 인해서 각 설정값은 정확히 일치할 수 없고 여유의 값을 두어야 한다. 예를 들면 다음과 같다.


  • --executor-memory/spark.executor.memory 는 executor의 heap size를 결정하지만 JVM은 off heap 부분에 대해서도 사용을 하기 때문에 Yarn container는 이를 감안하여 공간을 설정해야 한다.   spark.yarn.executor.memoryOverhead 값을 통해서 추가공간을 확보하며 default 는  0.07 * spark.executor.memory  이다
  • yarn.scheduler.minimum-allocation-mb 과 yarn.scheduler.increment-allocation-mb 를 통해서 



B. 예제를 통해서 살펴보자


각각 16 cores 와 64GB 의 메모리를 가진 노드가 6개 있는 cluster 인경우 


yarn.nodemanager.resource.memory-mb = 63 * 1024

yarn.nodemanager.resource.cpu-vcores = 15


를 사용할 수 있다. 


Hadoop에서 자체적으로 띄우는 데몬들이나 OS에서 사용하는 리소스들이 있기 때문에 최소 1G와 1개의 Core는 남겨둔 상태이며 필요에 따라서는 여유를 더 많이 둬야 한다.


두가지 설정값을 예시로 비교해보면



1) --num-executors 6 --executor-cores 15 --executor-memory 63G 로 세팅한다면??


단순히 생각하면 이상적인 수치이다. 각 노드에서 Executor를 하나씩 띄우고,

각 Executor는 63G의 메모리와 15 core를 Full로 사용해서 작업한다. 

그러나 다음과 같은 이유로 사실은 비효율적인 세팅이 된다.


- 그림1에서 보는 것처럼 executor의 메모리는 Yarn Nodemanager resource memory 와 Container 보다 작아야 한다.


- AM이 기동되어야 하기 때문에 해당노드에서는 executor가 15 cores 를 사용할 수 없다.


- executor가 15 cores를 사용할 경우 HDFS I/O로 인해 성능의 병목이 생긴다.



2) --num-executors 17 --executor-cores 5 --executor-memory 19G 가 더 나을 듯 하다.

이유는 다음과 같다.


- 각 노드에서 3개씩 executors가 기동되고 하나의 노드에서는 2개가 기동되어 AM을 위한 공간이 확보된다.


- 각 노드에서 3개의 executors가 기동되기 때문에 63 / 3 = 21 이 되며 위에서 설명한 여유공간을 위해서 21 * 0.07 = 1.47 을 제외한 만큼 19G로 executor-memory 로 설정한다.


<기타>

실제로 YARN Application에서 모니터링을 해보면 spark.executor.memory로 설정한 값을 모두 사용하지는 않는다. 그 이유는 그림1에서 보는 것처럼 fraction수치를 코드내에서 곱해주도록 되어있기 때문이다.



<참고>

http://blog.cloudera.com/blog/2015/03/how-to-tune-your-apache-spark-jobs-part-2/

'BigData' 카테고리의 다른 글

Spark에서 Hive ACID Table 접근시 오류  (0) 2017.10.12
Apache hive - transaction  (0) 2017.09.26
Hadoop Security for Multi tenant #4  (0) 2017.04.03
Hadoop Security for Multi tenant #3  (0) 2017.03.24
Hadoop Securiy for Multi tenant #2  (0) 2017.01.20

<개요>

 - Apache Kylo에서 Transformation 이나 Visual Query 수행시 Hive ACID Table에 접근할 경우 오류 발생


java.lang.RuntimeException: serious problem
        at org.apache.hadoop.hive.ql.io.orc.OrcInputFormat.generateSplitsInfo(OrcInputFormat.java:1021)
        at org.apache.hadoop.hive.ql.io.orc.OrcInputFormat.getSplits(OrcInputFormat.java:1048)
        at org.apache.spark.rdd.HadoopRDD.getPartitions(HadoopRDD.scala:199)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
        at scala.Option.getOrElse(Option.scala:120)
        at org.apache.spark.rdd.RDD.partitions(RDD.scala:237)
        at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
        at scala.Option.getOrElse(Option.scala:120)
        at org.apache.spark.rdd.RDD.partitions(RDD.scala:237)
        at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
        at scala.Option.getOrElse(Option.scala:120)
        at org.apache.spark.rdd.RDD.partitions(RDD.scala:237)
        at org.apache.spark.rdd.MapPartitionsRDD.getPartitions(MapPartitionsRDD.scala:35)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:239)
        at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:237)
        at scala.Option.getOrElse(Option.scala:120)
        at org.apache.spark.rdd.RDD.partitions(RDD.scala:237)
        at org.apache.spark.SparkContext.runJob(SparkContext.scala:1929)
        at org.apache.spark.rdd.RDD$$anonfun$collect$1.apply(RDD.scala:927)
        at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:150)
        at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:111)
        at org.apache.spark.rdd.RDD.withScope(RDD.scala:316)
        at org.apache.spark.rdd.RDD.collect(RDD.scala:926)
        at org.apache.spark.sql.execution.SparkPlan.executeCollect(SparkPlan.scala:166)
        at org.apache.spark.sql.execution.SparkPlan.executeCollectPublic(SparkPlan.scala:174)
        at org.apache.spark.sql.hive.HiveContext$QueryExecution.stringResult(HiveContext.scala:635)
        at org.apache.spark.sql.hive.thriftserver.SparkSQLDriver.run(SparkSQLDriver.scala:64)
        at org.apache.spark.sql.hive.thriftserver.SparkSQLCLIDriver.processCmd(SparkSQLCLIDriver.scala:311)
        at org.apache.hadoop.hive.cli.CliDriver.processLine(CliDriver.java:376)
        at org.apache.spark.sql.hive.thriftserver.SparkSQLCLIDriver$.main(SparkSQLCLIDriver.scala:226)
        at org.apache.spark.sql.hive.thriftserver.SparkSQLCLIDriver.main(SparkSQLCLIDriver.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:731)
        at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:181)
        at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:206)
        at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:121)
        at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: delta_0000000_0000000 does not start with base_
        at java.util.concurrent.FutureTask.report(FutureTask.java:122)
        at java.util.concurrent.FutureTask.get(FutureTask.java:192)
        at org.apache.hadoop.hive.ql.io.orc.OrcInputFormat.generateSplitsInfo(OrcInputFormat.java:998)
        ... 44 more


<내용>

 - Hive ACID Table의 경우 ORC 포멧을 이용하여 BASE File과 Delta File의 형태로 처리한다.

  (http://icthuman.tistory.com/entry/Apache-hive-transaction 참고)


 - Spark에서 파일을 읽으려고 하는데 최초에는 Base File이 존재하지 않는다.

  (테이블 생성후 insert를 하면 Delta File만 생성이 된다.)


<해결방안>

 - 수동으로 Major Compaction을 수행하면 Base File이 만들어지기 때문에 정상적으로 Spark에서 처리가 가능하다.

 

ALTER TABLE table_name [PARTITION (partition_key = 'partition_value' [, ...])]

  COMPACT 'compaction_type'[AND WAIT]
  [WITH OVERWRITE TBLPROPERTIES ("property"="value" [, ...])];


<참고>

https://issues.apache.org/jira/browse/HIVE-15189

https://issues.apache.org/jira/browse/SPARK-16996






네이버 블로그

LinkedIn


https://github.com/ggthename

'개인연결사이트' 카테고리의 다른 글

벡엔드 개발자 로드맵  (0) 2021.04.06

<개요>

Apache Hive는 HDFS에 저장되어 있는 파일데이터를  SQL 기반으로 처리할 수 있도록 하는 오픈소스이다. (모든 SQL을 지원하는 것은 아니며, 파일시스템 특성상 UPDATE, DELETE는 권장하지 않는다. )

그러나 지속적으로 DataWareHouse  트랜잭션 처리에 대한 요구사항이 꾸준히 생겨서 Hive에서도 트랜잭션을 지원하기 위한 기능이 개발되었다.

이에 대해서 내부구조를 간략히 살펴본다. (원문해석 + 개인이해/경험추가 )


<ACID>

database transaction의 4대 속성이라고도 하는데 다음과 같다.

https://ko.wikipedia.org/wiki/ACID


Atomicity(원자성)

트랜잭션과 관련된 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는 능력이다. 예를 들어, 자금 이체는 성공할 수도 실패할 수도 있지만 보내는 쪽에서 돈을 빼 오는 작업만 성공하고 받는 쪽에 돈을 넣는 작업을 실패해서는 안된다. 원자성은 이와 같이 중간 단계까지 실행되고 실패하는 일이 없도록 하는 것

Consistency(일관성)

트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지하는 것을 의미한다. 무결성 제약이 모든 계좌는 잔고가 있어야 한다면 이를 위반하는 트랜잭션은 중단

Isolation(고립성)

트랜잭션을 수행 시 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장하는 것을 의미한다. 이것은 트랜잭션 밖에 있는 어떤 연산도 중간 단계의 데이터를 볼 수 없음을 의미한다. 은행 관리자는 이체 작업을 하는 도중에 쿼리를 실행하더라도 특정 계좌간 이체하는 양 쪽을 볼 수 없다. 공식적으로 고립성은 트랜잭션 실행내역은 연속적이어야 함을 의미한다. 성능관련 이유로 인해 이 특성은 가장 유연성 있는 제약 조건이다

Durability(지속성)

공적으로 수행된 트랜잭션은 영원히 반영되어야 함을 의미한다. 시스템 문제, DB 일관성 체크 등을 하더라도 유지되어야 함을 의미한다. 전형적으로 모든 트랜잭션은 로그로 남고 시스템 장애 발생 전 상태로 되돌릴 수 있다. 트랜잭션은 로그에 모든 것이 저장된 후에만 commit 상태로 간주될 수 있다.


<Hive-ACID>

Hive 0.13이전에는 원자성, 일관성, 지속성에 대해서는 파티션 레벨로 지원하기 시작했으며, 고립성에 대해서는 Zookeeper나 메모리상에서 지원하는 locking 메커니즘 수준으로 제공되었으나 이제는 row level로는 ACID를 만족시킬 수 있다.

그러나 아직은 제약사항이 좀 있다.


1. 제약사항

- BEGIN, COMMIT, ROLLBACK을 지원하지 않는다. 모두다 auto-commit이다

그렇다 보니 Spring과 연계하여 사용할 때 약간의 불편함이 있다. Spring 에서 지원하는 @JdbcTest 를 사용할 경우 단위테스트 작성시 rollback기능등을 이용하여 각 메소드별 테스트시 데이터정합성이 깨지지 않도록 보장해주는 기능이 있는데 해당 기능이 사용불가능 하다. 



- ORC포멧만 지원한다. 

- Bucket설정이 되어야 한다. 또한 External Table의 경우 compactor가 제어할 수 없기 때문에 ACID테이블로 만들 수 없다.


- non-ACID session에서는 ACID Table에 대한 읽기/쓰기를 할 수 없다. 

  어찌보면 당연하다. ACID를 사용하기 위해서는 org.apache.hadoop.hive.ql.lockmgr.DbTxnManager 로 hive transaction manager를 변경해야 한다.


- Dirty read, read committed, repeatable read, serializable의 isolation level은 지원하지 않는다.

- 기존의 zookeeper나 in-memory 락 메커니즘은 호환불가능



2. 기본 설

처음에 이야기한 것처럼 HDFS는 오직 통으로 움직이는 것을 좋아한다.

그래서 transaction들의 특징을 만족시키기 위해서 아래와 같이 설게되었다.


테이블이나 파티션은 Base 파일의 집합으로 저장하고, 새로운 레코드나 update, delete에 대해서는 Delta 파일로 저장한다. 델타파일들의 집합이 만들어지면 읽는 시점에 합친다.



<Compactor>

ACID를 지원하기 위해서 Meatastore에서 수행되고 있는 background processes들을 compactor라고 한다.


Hive는 Base File과 Delta File을 이용한 Compaction으로 ACID를 지원하기 때문에  Table을 생성할때 


STORED AS ORC 와 

CLUSTERED BY XXX INTO N BUCKETS  를 반드시 추가해야 한다.


apache orc 사이트에 가면 Hive ACID를 어떤 형태로 Support 하고 있는지 상세하게 설명이 되어있다.


<그림 1 : Hive Table>



<그림 2 : Major & Minor compaction>



Delta파일들이 늘어나게 되면 성능을 위해서 compaction을 수행하게 되는데 유형은 다음과 같다.


- minor compaction : Delta파일들을 모아서 Bucket당 하나의 Delta파일로 rewrite한다.

- major compaction : 하나이상의 Delta파일과 Bucket당 Base파일을 가지고 Bucket당 새로운 Base파일로 rewrite한다. 


실제로 YARN 에서 조회하면 UPDATE,DELETE를 수행할때마다 YARN Application이 수행되지는 않으며

주기적으로 수행되는 compactor작업을 확인할 수 있다.



<Transaction/Lock Manager>

추후 상세설명



3. Configuration

Client Side

Server Side (Metastore)



4. Table Properties

ACID를 사용하기 위해서는 (update,delete) "transactional=true" 를 설정해야 한다.

그리고 한번 ACID테이블로 생성하면 되돌릴 수 없으니 주의해야 한다.

또한 hive-sitem.xml등에서 hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager 로 지정해야 하며 이 txManager가 없이는 ACID테이블을 사용할 수 없다.



Example: Set compaction options in TBLPROPERTIES at table level
CREATE TABLE table_name (
  id                int,
  name              string
)
CLUSTERED BY (id) INTO 2 BUCKETS STORED AS ORC
TBLPROPERTIES ("transactional"="true",
  "compactor.mapreduce.map.memory.mb"="2048",     -- specify compaction map job properties
  "compactorthreshold.hive.compactor.delta.num.threshold"="4",  -- trigger minor compaction if there are more than 4 delta directories
  "compactorthreshold.hive.compactor.delta.pct.threshold"="0.5" -- trigger major compaction if the ratio of size of delta files to
                                                                   -- size of base files is greater than 50%
);



그리고 2번에서 설명한 것처럼 compaction으로 ACID를 지원하기 때문에 compator에 대한 많은 상세설정이 있으며 compactor에 대한 설정은 ALTER문을 통해서 변경할 수 있다.


Example: Set compaction options in TBLPROPERTIES at request level
ALTER TABLE table_name COMPACT 'minor' 
   WITH OVERWRITE TBLPROPERTIES ("compactor.mapreduce.map.memory.mb"="3072");  -- specify compaction map job properties
ALTER TABLE table_name COMPACT 'major'
   WITH OVERWRITE TBLPROPERTIES ("tblprops.orc.compress.size"="8192");         -- change any other Hive table properties




<참고사이트>

https://cwiki.apache.org/confluence/display/Hive/Hive+Transactions

https://orc.apache.org/docs/acid.html


+ Recent posts