저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의

암바리(https://ambari.apache.org/)는 오픈소스 하둡 관리 도구이다. 설치부터, 설정 관리, 오퍼레이션, 모니터링, 통지 등 하둡 클러스터를 관리하는데 필요한 기본 기능을 제공한다. 암바리를 사용하다보면 레거시 환경의 통지 시스템을 이용해서 암바리의 문제 상황을 문자나 메신저 등으로 통지 받고 싶을 때가 있다. 하지만 아쉽게도 암바리는 이메일과 SNMP 트랩만 이용해서 문제를 통지하기 때문에, 암바리 Alert을 레거시 환경에 바로 붙일 수 없다.


왓츠업과 같이 SNMP를 지원하는 도구를 이용해서 이미 모니터링을 하고 있다면 이를 사용하면 된다. SNMP 기반의 모니터링 도구가 없거나 적합하지 않은 경우에는 암바리가 발생하는 SNMP 트랩을 직접 수신해서 처리하는 방식으로 레거시와 연동을 처리해야 한다.


[암바리 SNMP Alert 설정 화면]


암바리의 Alert SNMP 트랩 구성


암바리는 Alert 발생시 SNMP Alert 설정 화면에서 입력한 정보를 이용해서 트랩을 발생시킨다.

  • OID : 1.3.6.1.6.3.1.1.5.4와 같은 OID 문자열
  • Hosts : 트랩을 수신할 SNMP 트랩 리시버 서버의 호스트
  • Port : 리시버 서버가 사용할 포트 번호 (UDP 포트)
OID 값으로 "1.3.6.1.6.3.1.1.5.4"를 입력한 경우 트랩은 다음과 같이 구성된다.
  • OID(1.3.6.1.6.3.1.1.4.1.0), OBJECT IDENTIFIER[6] = 1.3.6.1.6.3.1.1.5.4
  • OID(1.3.6.1.6.3.1.1.5.4), OCTET STRING[4] = 상세메시지(body)
  • OID(1.3.6.1.6.3.1.1.5.4), OCTET STRING[4] = 요약메시지(subject)

상세 메시지는 몸체 내용에 해당하며, 다음과 같은 구조의 메시지를 갖는다. (마지막 줄에 공백 문자가 존재한다.)


\n

[Alert] Supervisor Process\n

[Service] STORM\n

[Component] SUPERVISOR\n

[Host] skt-phddtn3\n

\n

TCP OK - 0.000s response on port 56431\n

    


요약 메시지는 다음과 같은 구조를 갖는다.(마지막 줄에 공백 문자가 존재한다.)


\n

      [OK] Supervisor Process\n

    


요약 메시지는 Alert의 심각도로 시작한다. 심각도에는 OK, WARNING, CRITICAL, UNKNOWN이 존재한다.


트랩 리시버는 OID로부터 값을 읽어와 메시지를 알맞게 변환한 뒤 심각도에 따라 알맞은 처리를 하면 된다. 예를 들어, 심각도가 CRITICAL이면 SMS로 문자를 발송하고, WARNING이면 메신저로 메시지를 보내는 등의 작업을 하면 된다.


SNMP4J를 이용한 암바리 트랩 수신


트랩을 받기 위한 SNMP 도구가 따로 없다면 직접 트랩 리시버를 구현해야 한다. 다행히 SNMP 자체를 잘 몰라도 SNMP4J(http://www.snmp4j.org/)를 사용하면 쉽게 암바리 Alert 처리를 위한 트랩 리서버를 구현할 수 있다. 다음은 SNMP4J를 이용한 트랩 리시버를 구현한 코드 예이다.


public class TrapReceiver {

    private int port;

    private MultiThreadedMessageDispatcher dispatcher;

    private Snmp snmp = null;

    private Address listenAddress;

    private ThreadPool threadPool;


    private NotificationHandler notificationHandler;

    private int threadPoolSize;


    public TrapReceiver(int port) {

        this.port = port;

        this.threadPoolSize = 10;

    }


    public void init() throws IOException {

        threadPool = ThreadPool.create("Trap", threadPoolSize);

        dispatcher = new MultiThreadedMessageDispatcher(

              threadPool, new MessageDispatcherImpl());

        listenAddress = GenericAddress.parse("udp:0.0.0.0/" + port);

        TransportMapping<?> transport = 

                new DefaultUdpTransportMapping((UdpAddress) listenAddress);


        snmp = new Snmp(dispatcher, transport);

        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());

        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());


        snmp.listen();


        snmp.addCommandResponder(new CommandResponder() {

            @Override

            public void processPdu(CommandResponderEvent event) {

                handleNotification(event);

            }

        });

    }


    private void handleNotification(CommandResponderEvent event) {

        if (notificationHandler == null) {

            return;

        }

        notificationHandler.handle(createNotification(event));

    }


    private Notification createNotification(CommandResponderEvent event) {

        OID trapOID = (OID) event.getPDU().getVariable(SnmpConstants.snmpTrapOID);

        List<String> values = new ArrayList<>(2);

        Vector<? extends VariableBinding> varBinds = event.getPDU().getVariableBindings();

        if (varBinds != null && !varBinds.isEmpty()) {

            Iterator<? extends VariableBinding> varIter = varBinds.iterator();

            while (varIter.hasNext()) {

                VariableBinding var = varIter.next();

                if (var.getOid().equals(trapOID)) {

                    values.add(var.getVariable().toString());

                }

            }

        }

        return new Notification(values.get(0), values.get(1));

    }


    public void close() {

        try {

            snmp.close();

        } catch (IOException e) {

        }

        threadPool.stop();

    }


    public void setNotificationHandler(NotificationHandler notificationHandler) {

        this.notificationHandler = notificationHandler;

    }

}


이 코드에서 createNotification() 메서드가 트랩 메시지를 처리하는 코드이다. 암바리가 생성하는 트랩은 지정한 OID에 해당하는 첫 번째 변수에 상세 내용(몸체), 두 번째 변수에 요약 내용(제목)을 메시지로 전달한다. 따라서, 지정 OID를 갖는 메시지 값을 차례대로 저장한 뒤 각각 순서대로 몸체와 제목으로 사용하면 된다. 제목은 [OK], [CRITICAL]는 심각도를 포함하고 있으므로 이 값을 이용해서 트랩을 어떻게 처리할지 결정할 수 있다.


몸체 메시지에는 서비스 종류, 호스트 정보 등이 포함되어 있으므로 이 정보를 사용해서 서비스 담당자별로 통지하거나 장비 담당자에게 메시지를 보내는 것과 같은 처리를 할 수 있다.


Posted by 최범균 madvirus

댓글을 달아 주세요

오늘 Ambari 1.6을 이용해서 HDP 2.1을 설치하는데, Application Timeline Serve가 올라오지 않아 서버에 들어가 로그 파일에 기록된 에러 메시지를 봤더니, 다음 클래스를 찾지 못한다는 에러 메시지가 출력되고 있었다.


2014-07-16 19:01:55,808 INFO  applicationhistoryservice.ApplicationHistoryServer (SignalLogger.java:register(91)) - registered UNIX signal handlers for [TERM, HUP, INT]

2014-07-16 19:01:55,960 INFO  service.AbstractService (AbstractService.java:noteFailure(272)) - Service org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer failed in state INITED; cause: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassNotFoundException: Class org.apache.hadoop.yarn.server.applicationhistoryservice.timeline.LeveldbTimelineStore not found

java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassNotFoundException: Class org.apache.hadoop.yarn.server.applicationhistoryservice.timeline.LeveldbTimelineStore not found

        at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:1927)

        at org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer.createTimelineStore(ApplicationHistoryServer.java:165)

        at org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer.serviceInit(ApplicationHistoryServer.java:80)

        at org.apache.hadoop.service.AbstractService.init(AbstractService.java:163)


약간의 구글링을 통해서 위 클래스 이름을 다음과 같이 변경해주어야 한다는 것을 알았다. Ambari 1.6-46, Hadoop 2.4.0.2.1 기준으로 YARN -> Config 에서 "yarn.timeline-service.store-class" 속성의 값을 변경해주면 된다.


org.apache.hadoop.yarn.server.timeline.LeveldbTimelineStore


예전에 받아 놓은 HDP 2.1 Sandbox 버전의 경우, 위 속성 값이 org.apache.hadoop.yarn.server.applicationhistoryservice.timeline.LeveldbTimelineStore로 설정되어 있는데, 하둡 버전은 2.4.0.2.1로 동일하다. (같은 하둡 버전인데, 클래스 이름이 다르다는 건 좀...)

Posted by 최범균 madvirus

댓글을 달아 주세요

오늘 Ambari 1.5.1을 이용해서 호튼웍스의 HDP 2.1을 설치하던 도중 다음과 같은 에러가 발생했다.


'ambari.clusterconfig' doesn't exist


이 문제는 MySQL의 데이터베이스를 UTF-8로 만들면서 발생했다. ambari가 사용할 DB로 기존에 설치되어 있던 MySQL 5.1 버전을 사용했고, 다음 스크립트를 이용해서 데이터베이스를 생성했다.


create database ambari character set utf8


ambari-server setup을 이용해서 ambari가 사용할 DB를 설정할 때, 위 코드로 생성한 DB를 설정해주었다. setup 과정에서 아무 에러 메시지 출력 없이 정상적으로 실행되었다.


ambari-server start로 ambari 서버를 실행하고, http://설치호스트:8080 으로 접속해서 설치를 시작했다. 설치 대상 서버부터, 각 구성 요소를 어느 서버에 설치할지, hive 등의 메타 정보를 어느 DB에 보관할지 등을 설정했고, 최종적으로 설치를 시작했다. 그런데, 앞서 언급했던 ['ambari.clusterconfig' doesn't exist'] 에러 메시지가 출력되면서 더 이상 설치를 진행할 수 없게 됐다.


configcluster 테이블이 왜 존재하지 않는지 확인해 보기 위해, ambari가 DB 테이블을 생성할 때 사용하는 스크립트를 찾아보았다. 스 스크립트에서 configcluster 테이블을 생성할 때 사용되는 쿼리를 찾아서 mysql 콘솔에서 직접 실행해보니 다음과 같은 오류가 발생했다.


ERROR 1071 (42000): Specified key was too long; max key length is 1000 bytes


켁! PK 생성 제약으로 configcluster 테이블을 생성하지 못한 것이다. ambari-server setup 과정에서 아무 오류 없이 넘어갔기에 테이블 생성이 안 된 것을 설치 과정 후반부에 안 것이다.


위 문제가 발생한 이유는 데이터베이스를 UTF-8로 생성했기 때문이다. configcluster 테이블이 PK로 사용하는 컬럼은 세 개인데, 그 중 2개 컬럼의 타입이 varchar(255)이다. 그런데, UTF-8을 사용하면서 한 글자가 최대 4개 바이트까지 차지할 수 있기 때문에, 두 개 컬럼이 실제로 차지할 수 있는 바이트 수가 가뿐히 1000을 넘기게 된다.


이 문제를 해결하는 가장 쉬운 방법은 DB를 latin 계열 캐릭터셋을 사용하도록 만드는 것이겠지만, 그러고 싶지 않았다. 좀 검색을 해 보니 MySQL 5.5의 경우 innodb_large_prefix 옵션을 true로 주면 1000 바이트가 넘는 PK를 생성할 수 있다는 것을 알게 되었다. (http://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_large_prefix 참고)


[mysqld]

...다른설정들

innodb_large_prefix = true


기존에 설치된 MySQL 5.1에 아무것도 없었기에, 그냥 5.5로 바꾸고 위 옵션을 추가해 주었다. MySQL 5.1을 사용하고 있다면, utf-8이 아닌 euc-kr이나 다른 캐릭터 셋을 사용하도록 설정해서 PK의 바이트 길이가 1000을 넘지 않도록 해 줘야 할 것 같다.


Posted by 최범균 madvirus

댓글을 달아 주세요