2019년 11월 26일 화요일

하둡 데이터노드 추가/리밸런싱 하기 (Adding and Rebalancing Hadoop Datanodes)

하둡에 데이터 노드를 추가하는 경우는 공간이 모자라거나 성능향상이 필요할 때이다. 공간이 부족할 경우 어쩔 수 없이 노드를 추가해야하지만(아니면 과거 데이터를 지우거나) 그 외에 성능 확장(병렬처리)을 위해서라면 10%의 성능 향상을 위해 기존 노드 갯수의 10%만큼은 추가해주어야 한다고 알려져있다.



하둡에 신규 노드를 추가하고 리밸런싱을 통해 재분산 작업을 해보도록 하자.

작업순서는 크게 다음과 같다.

1. 신규 데이터 노드들에 자바, 하둡 설치
2. ssh 통신을 위한 인증키복사
3. 마스터 노드에서 slave를 인식할 수 있도록 slaves 파일에 slave host 추가
(하둡 버전에 따라서 masters, slaves 혹은 workers로 존재할 수 있다.)
4. 마스터 노드에서 BalancerBandwidth 변경
5. 마스터 노드에서 데이터 노드 rebalance 수행
0. 예상치 못한 상황이 생길 경우 먼저 처리.


각 노드에 기존 노드들과 동일한 자바를(zulu jdk) 설치하자.
기존에 갖고 있던 자바 설치파일을 scp로 옮긴다.
scp ./zulu8.38.0.13-ca-jdk8.0.212-linux_x64.tar.gz id@a.b.c.214:test/sw/java
scp ./zulu8.38.0.13-ca-jdk8.0.212-linux_x64.tar.gz id@a.b.c.215:test/sw/java
scp ./zulu8.38.0.13-ca-jdk8.0.212-linux_x64.tar.gz id@a.b.c.216:test/sw/java
...


압축을 푼다.
tar -zxvf zulu8.38.0.13-ca-jdk8.0.212-linux_x64.tar.gz
tar -zxvf zulu8.38.0.13-ca-jdk8.0.212-linux_x64.tar.gz
tar -zxvf zulu8.38.0.13-ca-jdk8.0.212-linux_x64.tar.gz
...

기존노드들 처럼 심볼릭 링크 jdk를 만든다.
ln -s /sw/java/zulu8.38.0.13-ca-jdk8.0.212-linux_x64 jdk

JAVA_HOME path 변경해준다.
~/.bashrc에 JAVA_HOME과 HADOOP_HOME(미리 세팅)해 놓는다.
그리고 각각 환경에 맞게 필요한 것이 있다면 적용하도록 한다.





source .bashrc로 적용해서 echo $JAVA_HOME이 zulu가 잡히면 성공이다.

자바 세팅이 끝났다. 똑같은 방법으로 하둡을 설치한다.



하둡 설치 및 세팅
각 신규 노드는 별도로 4개의 디스크를 더 마운트하여 총 test01~5까지 있다.
먼저 /test01/hadoop 디렉토리 생성하고 test02~5까지 계층구조로 디렉토리 생성한다.
mkdir /test01/hadoop
mkdir -p /test02/hadoop/hdfs/data/
mkdir -p /test03/hadoop/hdfs/data/
mkdir -p /test04/hadoop/hdfs/data/
mkdir -p /test05/hadoop/hdfs/data/



역시나 마찬가지로 기존에 쓰던 하둡을 압축해서 신규노드들로 옮겨서 설치한다.
(위에서 ~/.bashrc에서 하둡경로 세팅해놓은 곳으로 설치한다고 보면 된다.)


scp ./hadoop-2.7.1.tar.gz id@10.203.5.214:/data01/hadoop
scp ./hadoop-2.7.1.tar.gz moneymall@10.203.5.215:/data01/hadoop
scp ./hadoop-2.7.1.tar.gz moneymall@10.203.5.216:/data01/hadoop
...

압축을 푼다.
각 노드에서 tar -zxvf hadoop-2.7.1.tar.gz 

cd $HADOOP_HOME으로 가서 경로가 잘 잡혔는지 확인한다.
hdfs dfs -ls / 명령어로 설치가 잘 되었는지 확인한다.

hdfs 명령어로 하둡 깔린거 확인했고 홈 잡혀있는거 확인했고 잘된것 같다.




다음은 ssh 통신을 위한 인증키를 복사하도록 하자.
~/.ssh 만들기
cd ~
mkdir .ssh
chmod 700 .ssh
cd .ssh

기존에 뚫어놓은 키 가져오기
아무데서나 가져와도 된다. 어차피 최초 설치 시 마스터가 뿌린것이기 때문이다.
scp authorized_keys id@a.b.c.201:/~~path~~/.ssh

다음처럼 키 내용이 제대로 들어있는지 확인하고 제대로 들어있으면 정상이다.

귀찮아도 ssh로 신규 데이터노드에 한번씩 꼭 붙어보자. (비번을 물어보지 않을 때까지..)

다음은 마스터 노드의 slaves 파일에 신규노드 추가 명시하자. 현재 하둡 2.7버전이고 여기에는 masters, slaves 파일이 존재했다. slaves파일에 신규 호스트들을 추가한다.


작업이 거의 끝나간다.

마스터에서 datanode refresh를 해준다.

hdfs dfsadmin -refreshNodes


기존 slave 72대에 일단 테스트로 5대만 추가해서 리프레쉬한 결과 5대의 데드노드가 추가됨을 확인

실제로 리프레시 후 Dead Nodes에 추가되었는지 확인한다.


yarn에서 보면 바로 적용은 안되고 조금 있어야 바뀌는 것 같다.

이제 추가된 데이터노드를 하나씩 살린다.

cd $HADOOP_HOME
sbin/hadoop-daemon.sh start datanode
sbin/yarn-daemon.sh start nodemanager


전후 jps를 통해 문제 없이 잘 살아남을 확인했다.

마지막으로 기존 데이터를 분산시켜주기 위해 리밸런싱을 하도록 하자.
(며칠이 걸릴지 모른다.)

마스터노드에서 대역폭을 100메가로 변경한다.
hdfs dfsadmin -setBalancerBandwidth 104857600


리밸런싱 시작한다. 숫자 5는 각 노드간 차이가 5%이내로 함을 의미한다.
sbin/start-balancer.sh -threshold 5




실행하니까 신규 노드에 데이터가 차는것을 확인할 수 있다.



리밸런싱 로그를 tail 걸어서 확인해보면 잘 진행되고 있다. 언제 끝날려나..


추후 확인해보니.. 기존 노드의 용량이 4TB씩이고 최대 3TB씩 사용하고있었다. 이를 5%씩 bandwidth 10mb로 분산시킨 결과 대략 2TB씩 골고루 나뉘어졌으며 이런 케이스에서는 약 15일 정도 소요되었다.

Cassandra + Prometheus + Grafana Monitoring System unsing by jmx exporter(node exporter)

카산드라(cassandra) 모니터링을 하기 위한 시스템을 마련하기 위한 포스팅이다. InfluxDB를 사용하는 케이스가 많이 있지만 이번 포스팅에서는 InfluxDB 없이 모니터링 시스템을 구축해본다.


큰 틀은 cassandra의 jmx를 활성시키고 해당 로그를 Prometheus에서 수집해서 Grafana로 화면에 그려주는 방식이다.


사용 버전
- cassandra 3.11.4
- jmx_prometheus_javaagent-0.3.0.jar
- prometheus 2.14.0
- grafana 5.0.1


그럼 카산드라에서 발생시키는 로그를 수집서버에서 수집해보자.
그 로그들은 각 클라이언트에서 수집서버로 Push(Polling)하는 방식과 수집서버에서 카산드라 로그를 Pulling하는 방식 두 가지로 나뉠 수가 있다. 

일반적인 모니터링 툴들은 각 클라이언트에서 수집서버로 로그를 전송하는 형태를 띄고 있다면 프로메테우스는 각 로그들을 각 클라이언트에서 exporter를 통해 가져오는 방식을 차용하고 있어서 프로메테우스가 장애가 나더라도 서비스 어플리케이션에 문제가 되지 않는다. 왜냐하면 각 클라이언트 들은 메트릭 정보를 수집해놓고 수집해가기를 기다리기만 하기 때문에 프로메테우스의 장애와는 아무런 상관이 없다.


아래는 프로메테우스의 아키텍쳐이다.


간단하게 살펴보면 프로메테우스 서버가 있고 여기서는 PushgaeWay나 Jobs/exporters에서 메트릭정보를 Pulling 하게 된다. 즉 어플리케이션단에서 exporter를 띄워놓거나 Pushgateway로 메트릭 정보를 보내면 프로메테우스 서버에서는 저 메트릭 정보를 가져오는 형태이다.
이 포스팅에서는 node exporter(서버 로그)와 jmx exporter(카산드라 로그)를 사용할 것이다.

그리고 Prometheus server에서 설정해놓은 정보에 따라 AlterManager에 시그널을 주면 이메일같은 채널로 알림기능을 설정할 수 있다.


마지막으로 Prometheus web UI나, Grafana와 같은 Visualization 툴을 통해 프로메테우스에서 수집한 메트릭 정보를 그려줄 수 있다. 이를 위해 PromQL이라는 쿼리를 사용하게 되는데 그라파나에서 제공하는 대시보드를 보면 sum이나 count가 대부분이라서 크게 어렵지 않는 것 같다. 그러니 누가 카산드라 메트릭 정보를 잘 표현할 수 있는 쿼리를 아주 잘 만들어주면 기쁜마음으로 사용하겠다.


카산드라를 설치하고 jmx까지 활성화가 되있다고 가정하자.
jmx 활성화 하는 방법은 어렵지 않다.
필자의 경우 중간에 제대로 동작을 안했었는데 이유는 conf/cassandra-env.sh 파일에서 LOCAL_JMX가 yes로 세팅되어 있었기 때문이다.
따라서 다음과 같이 수정하였다.

if [ "x$LOCAL_JMX" = "x" ]; then
    LOCAL_JMX=no
fi


그럼 이 데이터를 Prometheus server가 가져갈 수 있도록 jmx exporter와 node exporter를 설치하도록 한다.


이 포스팅에서는 두 가지 모두 설치해보고 비교해보자.

먼저 node exporter 세팅이다.

프로메테우스 공식 홈페이지에서 tarball을 받아서 각 노드에 옮겨서 설치한다.

주소는 다음과 같다.
https://prometheus.io/docs/guides/node-exporter/

node exporter는 단순히 압축을 풀어서 실행하기만 하면 9100 포트로 수집된다.
따라서 prometheus server에서 각 노드 9100 port로 들어가서 수집해오면 된다.

UI로 들어가서 확인해보자.



다음은 prometheus server에서 node exporter를 수집해가면 된다.
prometheus.yml 파일에 수집타겟을 설정해주자.


프로메테우스 UI에 가서 targets를 확인해보면 잘 떠있는 것을 확인할 수 있고 실제로 수집 되는 데이터를 그래프로 확인도 가능하다.


테스트로 그래프를 그려본 결과 그려지긴 하지만 뭔가 다이나믹하지 않고 상당히 아쉬운 느낌이 든다.


따라서 위의 프로메테우스 결과를 다시 grafana로 가져와서 grafana를 통해 그려보도록 한다.


이 역시 공식 사이트에 들어가서 다운을 받아서 설치하도록 하자.
주소는 다음과 같다. https://grafana.com/grafana/download


이 역시 압축을 풀고 서버를 띄우기만 하면되서 간단하다.
서버가 올라오면 3000번 포트로 접속해보자.


누군가 만들어서 배포해놓은 대시보드를 사용하자.
https://grafana.com/grafana/dashboards/


아래는 대시보드 11074번을 import 하였다.



그 결과 다음과 같은 아름다운 화면이 완성되었다.




하지만 뭔가 아쉬운 점이 있다면 node exporter는 하드웨어 모니터링 느낌이 강하다.
우리는 카산드라 노드의 정보가 알고싶기 때문이다.

이를 위해서 jmx를 써야한다.
같은 방식으로 jmx exporter 세팅 후 프로메테우스+그라파나로 연동하자.


cassandra metric을 보기 위한 jmx exporter 세팅이다.

다운로드 링크
https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.3.0/

카산드라는 jmx 세팅을 위해 카산드라를 노드별로 한대씩 내렸다가 올려야하기 때문에 조금 귀찮을 수 있어서 한번에 성공하도록 한다.

jmx exporter가 사용할 cassandra.yml 파일은 아래 주소를 참고 했다.
혹시 바뀔수도 있으니 현재 시점 샘플을 포스팅 마지막에 구글 드라이브 링크로 첨부하였다.
https://raw.githubusercontent.com/prometheus/jmx_exporter/master/example_configs/cassandra.yml


저 두 파일을 카산드라 폴더에 넣고 conf/cassandra-env.sh에 JVM_OPTS에서 물고 올라갈 수 있도록 다음을 추가하자.

JVM_OPTS="$JVM_OPTS -javaagent:/sw/cassandra/jmx_prometheus_javaagent-0.3.0.jar=7070:/sw/cassandra/cassandra.yml"


또는 다음처럼 명령어로 추가를 해주자. 모든 수집대상 노드에서 똑같이 반복한다.
echo 'JVM_OPTS="$JVM_OPTS -javaagent:'$PWD/jmx_prometheus_javaagent-0.12.0.jar=7070:$PWD/cassandra.yml'"' >> conf/cassandra-env.sh


위처럼 conf/cassandra-env.sh 마지막 줄에 jmx 에이전트 세팅을 해주고 카산드라를 재기동 한다.

역시 프로메테우스에서도 target ip를 추가해주자.
(이후 dashboard를 하나도 수정하지 않기 위해 job_name을 cassandra_로 시작하도록 하였다. instance명을 반드시 cassandra_로 맞춰주도록 하자.)

위에서 JVM_OPTS 를 설정하면서 7070 포트로 설정했으니 이번에는 7070 포트로 수집을 하면 된다.



프로메테우스 target에서도 up 상태를 확인할 수 있다.


node exporter와는 다르게 cassandra metric이 수집되고 있다.




마지막으로 grafana에서 적당한 대시보드를 골라서 그려보도록 하자.

dashboard number 5408을 활용했다. (https://grafana.com/grafana/dashboards/5408)

datasource가 없다면 생성해주고 5408 json을 import하면 다음과 같은 화면이 그려진다.



이상으로 cassandra + jmx exporter(node exporter) + prometheus + grafana를 활용한 cassandra monitoring system 구축을 마친다.

관련 파일은 아래 구글드라이브에서 첨부하였다.
https://drive.google.com/open?id=16UtW5A175w1tVknXvHFTOQUU9W2d9bNS

내용물
1. cassandra.yml
2. cassandra_rev3.json
3. node_exporter-0.18.1.linux-amd64.tar.gz
4. jmx_prometheus_javaagent-0.3.0.jar








2019년 11월 8일 금요일

Artificial Intelligence - Apriori Algorithm, support, confidence, lift

연관규칙분석, Assosiation Rule(장바구니 분석)은 어떤 데이터들 간에 연관성을 보는 방법이다. Assosiation Rule은 어떤 ITEM 집합의 존재가 다른 ITEM 집합의 존재를 암시하는것을 의미하고 A => B (A entails B) 라고 할 수 있겠다. (이는 인과관계가 아니라 상관관계이다.)
그리고 이를 통해 교차판매(Cross selling), 묶음판매, 부정행위 적발 등에서 사용할 수 있다.

이번 포스팅에서는 support, confidence, lift 활용법과 Apriori 알고리즘에 대해서 포스팅하며 실제로 어떻게 사용할 수 있을지 간단한 예를 들어 설명하도록 한다.


먼저 연관규칙생성 시 사용할 수 있는 결정도구는 3가지로 정리할 수 있다.
1. support(지지도)
2. confidence(신뢰도)
3. lift(향상도)

실제로는 위의 3가지를 섞어서 사용해도되고 하나만 사용해도되지만 반드시 데이터를 관찰해가며 적당한 도구와 최소 수치를 정할 수 있도록 해야한다.


Apriori Algorithm은 support를 활용하여 Assosiation Rule를 구하는데 알고리즘 설명에 앞서 연관규칙생성에 사용할 수 있는 도구의 의미를 알아보자.


먼저 support는 지지도로서 정의는 다음과 같고 이는 사건 A가 일어날 확률로 표현된다.
이는 frequent item sets을 판별하는데 사용이 된다.


예를 들어서 A와 B의 support를 구하기 위해서는 (A와 B가 동시에 발생한 사건 수)/(전체 사건 수)가 된다.

다음은 confidence는 신뢰도로서 다음과 같이 정의할 수 있다. 이는 사건 A가 주어졌을 때 B사건이 일어날 조건부 확률로 표현되며 아이템 집합 간의 연관성 강도를 측정하는데 사용된다. 쉽게 풀어보면 사건 A가 일어났을 때 사건 B도 함께 일어난 확률이라고 볼 수 있다.


예를 들어서 A와 B의 confidence를 구하기 위해서는 (A와 B가 동시에 발생할 확률)/(A가 일어난 확률)가 되고 이는 A가 발생했을 때 이 중에서 B가 얼마나 발생할지를 나타낸다.
아무래도 전체중에 A, B가 동시에 발생할 확률보다는 더 연관도가 높다고 할 수 있다. 하지만 수식에서 보는 것과 같이 A를 구매했을 때 B를 구매하는 것과 B를 구매했을 때 A를 구매하는 것은 다를 수 있어서 선후행 관계를 파악할 수 있다.


그리고 실제로 효용가치가 있는지 판별하기 위해 사용하는 lift는 향상도로서 조건절과 결과절이 서로 독립일 때와 비교하여 두 사건이 얼마나 함께 발생하는지를 나타낸 확률이다.


이는 실제 발생확률을 각 사건의 발생이 독립일 경우에 비해 그 사건이 동시에 발생할 예상기대 확률로 나눈 것을 뜻하며 수식을 정리하면 (A가 발생하고 B가 발생할 확률)/(B가 발생할 확률)로 나타낼 수 있다.

여기서 lift = 1 이면 조건절(Antecedent)과 결과절(Consequent)은 서로 독립이고 lift > 1 인 경우 서로 양의 상관관계로서 서로 연관이 있다고 판단할 수 있으며 lift < 1 인 경우는 음의 상관관계로서 연관이 없다고 판단할 수 있다. 하지만 이는 반드시 데이터를 보고 판단해야하며 lift < 1 인 경우가 무조건 틀리다고 볼 수 없다.



이제 Apriori Algorithm의 예시를 보도록 하자.
여기서는 frequent item sets을 고르기 위해 support를 사용했다.

다음과 같이 원천 데이터가 있다고 가정하자.


id는 구매자(또는 트랜잭션, 주문)이고, items는 상품셋이다.
최소 조건을 support > 1로 가정했다. (확률이나 경우의 수나 같다.)
전체 구매 상품중에 A 2번, B 3번, C 3번, D 1번, E 4번이 등장했고 D는 support > 1에 의해 제거한다.


남은 A, B, C, E로 조합을 만들어보면 다음과 같이 나타낼 수 있으며 발생 횟수(support)는 다음과 같다.


여기에서도 마찬가지로 {A, B}, {A, E}가 제거되며 남은 A, B, C, E로 조합을 만들면 {A, B, C}, {A, B, E}, {A, C, E}, {B, C, E} 4가지가 나오는데 직전 단계에서 {A, B}와 {A, E}가 제거되었기 때문에 볼 필요가 없이 {A, B, C}, {A, B, E}, {A, C, E}, {B, C, E} 중에 {B, C, E}만 남게 된다.


이를 제거하는 이유는 {A, B, C}가 나올 확률이 아무리 커봤자 {A, B}가 나올 확률보다 작거나 같기 때문이다. 이를 초월 집합 제거라고 부르며 수준 미달의 초월집합을 제거함으로써 효율적인 계산을 할 수 있게 된다.

이 결과가 의미하는 것은 B, C, E가 빈발 항목 집합, 즉 frequent item sets라는 것이다.

한번 쉽게 생각해보자.

만약 실제로 적용할 때에는 A를 구매했을 때 B를 추천하거나 C를 추천하는 경우가 많기 때문에 frequent item sets의 조합 크기를 2로 생각하고 {A, C}, {B, C}, {B, E}, {C, E}를 추천셋으로 만들어놓고 support 값으로 정렬, lift로 2차 정렬을 하여 상품셋 추천을 구현해도 되지않을까 싶다.
개인적으로는 상품셋의 퀄리티 보다는 상품셋의 규모가 매출에 더 큰 영향을 준다고 생각하기 때문이다. 물론 일정 수준 이상의 성능(퀄리티)이 나오는 경우에 한해서이다.


그럼 support, confidence, lift를 모두 활용한 예를 들어보도록 하자.


다음과 같은 구매내역을 가정한다. 최소 support는 0.35로 가정했다.


이를 co-occurence matrix로 표현하면 다음과 같다.


단일항목 집합으로 보면 다음과 같이 support 값을 구할 수 있다.
최소 support에 의해 D와 E는 제거된다.


2개항목 집합으로 보면 다음과 같다.
결국 최소 support에 의해 {A, B}와 {B, C}만 남게된다.


이는 단순히 support로 frequent item sets을 구한 것이다.

이번엔 confidence(신뢰도)를 고려해보자. 최소 신뢰도는 0.75로 가정한다.



min confidence 0.75에 의해 A=>B이 제거되었다.
이는 A를 구매했을 때 B를 구매하는 것보다 B를 구매했을 때 A를 구매할 확률이 높다는 것이고 C를 구매했을 땐 A를 구매할 확률이 100%라는 것을 의미한다.

이번엔 lift를 고려해보자. confidence를 support로 나눠주면 된다. 즉 support와 confidence만 알면 lift는 어렵지 않게 구할 수 있다.



lift > 1이 양의상관관계이므로 0.9를 제거하면 lift 1.5짜리 두 세트가 남게된다.
B=>C 는 support 0.5, confidence 1.0, lift 1.5 이다.
C=>B 는 support 0.5, confidence 0.75, lift 1.5 이다.

특히 B=>C의 경우 support 0.5에 의해 B와 C를 동시에 구매한 사람은 절반이고, confidence 1.0에 의해 B를 구매한 사람들은 C도 100% 구매한 것이다. 또한 lift 1.5에 의해 B를 구매했을 때 C를 구매할 확률은 단순히 C를 구매했을때의 확률보다 1.5배나 높아진다.


확실한 것은 현업에 적용하기 위해서는 도메인 지식을 잘 활용하는 것과 데이터를 관찰해가며 사용해야한다는 것이다. 도메인 지식을 잘 활용한다는 것은 데이터 클린징 또는 전처리를 잘 해야한다는 것과 같은 의미로 받아들였으면 좋겠다.



-
다음 포스팅은 FP-Growth에 대해서 포스팅해볼까 한다.
FP-Growth는 FP-Tree구조를 활용해서 Apriori algorithm의 연산속도 문제를 개선한 알고리즘이다. 확실히 Apriori 알고리즘은 구현하기 쉽지만 연산속도가 문제인 것 같다. 실제로 비슷한 것을 프로시저(쿼리)로 짠 것을 적용해달라고 해서 검증하다보니 연산속도가 장난이 아니어서 2step으로 나누어 일배치+월배치로 타협을 본 적이 있다.



2022년 회고

 올해는 블로그 포스팅을 열심히 못했다. 개인적으로 지금까지 경험했던 내용들을 리마인드하자는 마인드로 한해를 보낸 것 같다.  대부분의 시간을 MLOps pipeline 구축하고 대부분을 최적화 하는데 시간을 많이 할애했다. 결국에는 MLops도 데이...