Presto User define function 만들기

Presto는 SQL on Hadoop 계열의 오픈소스 솔루션으로 Facebook 에서 개발하였으며, 현재 Netflix 등에서 사용하고 있는 솔루션이다. 메모리 기반으로 데이터를 처리함으로써 기존 Hive on MR 보다 아주 빠른 성능으로 SQL을 처리하며 다양한 스토리지에 저장된 데이터를 SQL을 이용하여 Join 할 수 있다.  자세한 내용은 다음 사이트 참고

https://prestodb.io/

Presto에서는 기본적으로는 사용자 정의 함수 기능을 제공하지 않는다. 일반적으로 SQL을 사용하는 데이터를 처리하는 솔루션에서 사용자 정의 함수를 제공하기 위해서는 다음 기능이 제공되어야 사용자는 불편함 없이 사용할 수 있다.

1. 사용자 정의 함수 등록 및 삭제
    - Global 함수나 세션별 함수
    - DDL을 이용하여 함수 등록(Hive의 경우 Create function 명령 이용)
2. 운영 중인 시스템의 중지 없이 배포 가능

1번의 경우 구현하는 것이 그렇게 어렵지는 않지만 분산된 환경에서 수십/수백대의 서버에 수많은 질의가 실행되는 도중에 새로운 버전의 로직을 배포하고 로딩하는 것은 쉽지 않다. Hive의 경우 실제 질의가 실행될 때 Task를 fork 하는 방식이기 때문에 질의 실행전 함수의 라이브러리를 배포한 다음 Classpath만 설정해주면 되기 때문에 상대적으로 쉽게 구현할 수 있다. 이것이 여러 SQL on Hadoop 중에서 사용자 정의 함수를 가장 잘 지원하는 솔루션 중의 하나이다.
Presto의 경우 위와 같은 기능의 사용자 정의 함수를 제공하지 않는다. 기능은 다수 부족하지만 Presto 코드를 수정하지 않고 사용자 정의 함수를 만들기 위해서는 Presto의 Plugin 기능을 이용하면 된다. Plugin은 주로 새로운 저장소를 Presto에 지원하기 위해 사용하는 기능으로 Connector를 만들때 사용한다. 기본적으로 제공되는 Connector는 Hive, Cassandra, Mongo, JDBC Basic, MySQL, Postgresql, Kafka, Raptor, Orc, Redis 등이 있다.
이 Pulgin 기능을 이용하여 Connector 부분은 제외하고 FunctionFactory 를 새로 정의하여 사용자 정의 함수를 구현할 수 있다.
다음은 간단한 사용자 정의 함수를 위한 클래스들이다.

public class BabokimPlugin implements Plugin {
  private Map<String, String> optionalConfig = ImmutableMap.of();
  private TypeManager typeManager;

  @Override
  public void setOptionalConfig(Map<String, String> optionalConfig) {
    this.optionalConfig = ImmutableMap.copyOf(requireNonNull(optionalConfig, "optionalConfig is null"));
  }
  @Inject
  public synchronized void setTypeManager(TypeManager typeManager) {
    this.typeManager = requireNonNull(typeManager, "typeManager is null");
  }
  @Override
  public <T> List<T> getServices(Class<T> type) {
    if (type == FunctionFactory.class) {
      return ImmutableList.of(type.cast(new BabokimFunctionFactory(typeManager)));
    }
    return ImmutableList.of();
  }
}

Plugin 클래스의 getServices() 메소드에서 Connector, FunctionFactor 등을 전달한다. 예를 들어 Connector가 추가 되었으면 다음과 같이 할 수 있다.
if (type == ConnectorFactory.class) {
    return ImmutableList.of(type.cast(new BabokimConnectorFactory("babokim", optionalConfig)));
}

다음은 FunctionFactory를 다음과 같이 만든다.
public class BabokimFunctionFactory implements FunctionFactory {
  private final TypeManager typeManager;
  public BabokimFunctionFactory(TypeManager typeManager) {
    this.typeManager = requireNonNull(typeManager, "typeManager is null");
  }
  public List<SqlFunction> listFunctions() {
    return new FunctionListBuilder(typeManager)
        .scalar(EchoFunctions.class)
        .getFunctions();
  }
}

이제 사용자 정의 함수를 만들 준비가 되었다. listFunctions() 에서 반환하고 있는 EchoFunctions 클래스 만들고 여기에 사용자 정의 함수를 구현하면 된다. 예제의 경우 간단하게 두개의 함수만 구현하기 위해 EchoFunctions 클래스만 반환하는데 여러 개의 클래스를 반환할 수도 있다.

public class EchoFunctions {
  public EchoFunctions() {
  }

  @Description("Echo message")
  @ScalarFunction("echo")
  @SqlType(StandardTypes.VARCHAR)
  public static String echo(@SqlType(StandardTypes.VARCHAR) Slice str) {
    return str.toStringUtf8();
  }

  @Description("Hello message")
  @ScalarFunction("hello")
  @SqlType(StandardTypes.VARCHAR)
  public static String hello(@SqlType(StandardTypes.VARCHAR) Slice str) {
    return "Hello " + str.toStringUtf8();
  }
}

여기서 마지막으로 META-INF/services에 다음과 같은 파일을 만들어서 plugin 정보를 저장한다.
파일명: com.facebook.presto.spi.Plugin
내용: plugin class
   예: kr.co.jaso.BabokimPlugin

이렇게 한 다음 jar 파일을 만들고 presto 서버의 plugin 디렉토리(babokim)에 배포한다. Presto를 재시작 한 다음 콘솔에서 다음과 같이 입력한다.

select echo('test');
select hello('test');

어떤 경우 사용자 정의 함수에서 특정 데이터베이스의 테이블에 저장된 코드 정보를 조회하여 메모리에 올려놓고 처리하는 경우가 있는데 이 경우 DB 접속 정보 등 환경설정이 필요하다. Connector의 경우 etc/catalog 디렉토리에 property 파일 만들고 관련 정보를 저장할 수 있지만 아직 Function에서 설정 정보를 가져오는 부분은 찾지 못했다.  그래서 약간 우회 전략으로 다음과 같이 처리하였다.
Plugin 클래스의 setOptionalConfig() 메소드의 파라미터로 전달되는 config 정보에서 다음 key 값을 읽어 오면 Presto의 etc/config.properties 파일에 대한 pull path가 전달되는데 이 path를 이용하여 etc 디렉토리의 위치를 가져온 다음에 처리하도록 하였다.

public void setOptionalConfig(Map<String, String> optionalConfig) {
  String babokimConfigFile = null;
  if (optionalConfig != null) {
    String configFilePath = optionalConfig.get("config");
    if (configFilePath != null) {
      babokimConfigFile = (new File(configFilePath)).getParentFile().getAbsolutePath();
    }
  }
  this.optionalConfig = ImmutableMap.copyOf(requireNonNull(optionalConfig, "optionalConfig is null"));
  File myConfigFile = new File(babokimConfigFile + "/" + "babokim.properties");
  //Load property
}

Posted by 김형준

ssh key 관리자만 또는 중앙 집중 관리하기

별도의 VPN 등을 설치하지 않고 특정 서버의 오픈된 포트에 접속하는 경우 ssh 터널링을 많이 이용합니다.
ssh 터널링을 사용하기 위해서는 특정 포트에 접근하고자 사용자의 ssh key를 서버에 등록하게 됩니다. 이렇게 되면 이미 등록된 키의 사용자가 다른 사용자의 key를 등록할 수 있는데  이것은 보안에 큰 헛점이 됩니다. ssh key를 관리자만 등록할 수 있게 설정하는 방법을 삽질을 했는데 다음과 같이 설정하면 가능합니다. 다른 간단한 방법이 있으면 알려주세요.

예시에서는 /etc/ssh/KEYS/$user/authorized_keys 에 특정 사용자의 key를 저장하고 이 디렉토리는 sshd 만 접근 가능하도록 설정하도록 구성

1. /etc/ssh/sshd_config 파일에서 설정 변경
- AuthorizedKeysCommand 설정 변경
이 값에 로그인 사용자의 ssh key를 반환하는 명령을 설정, 이 기능을 이용하면 git repository 또는 DB에 ssh key를 저장할 수도 있음. ssh daemon이 첫번째 파라미터로 사용자 ID를 전달함. 필자의 경우 다음과 같이 특정 디렉토리의 파일에서 읽어오는 script를 호출

  Example:
  AuthorizedKeysCommand /usr/sbin/get_user_keys
  다음은 /usr/sbin/get_user_keys 내용
   #!/bin/bash
   user=$1
   cat /etc/ssh/KEYS/$user/authorized_keys


- AuthorizedKeysCommandUser 설정 변경
이값은 AuthorizedKeysCommand를 실행 시키는 linux user에 대한 설정으로 sshd가 AuthorizedKeysCommand에 설정된 명령을 실행할 때 이 값에 설정된 사용자로 호출. 
   Example:
   AuthorizedKeysCommandUser nobody
    (nobody는 linux에서 이와 같은 용도로 사용하기 위해 이미 만들어져 있는 사용자)

2. 사용자가 추가될 때 마다 다음과 같은 스크립트 호출
add_user.sh
========================================
#!/bin/bash
user=$1
sudo adduser $user
sudo mkdir /etc/ssh/KEYS/$user
sudo touch /etc/ssh/KEYS/$user/authorized_keys
sudo chown nobody.nobody -R /etc/ssh/KEYS/$user
sudo chmod 700 /etc/ssh/KEYS/$user
sudo chmod 600 /etc/ssh/KEYS/$user/authorized_keys

여기서 중요한 사항은 생성된 디렉토리의 사용자는 AuthorizedKeysCommandUser 에서 설정된 사용자로 해야 하며 mode는 600으로 해야 함

3. /etc/ssh/KEYS/$user/authorized_keys 에 해당 사용자의 ssh key 추가

* 관리해야 할 서버가 많다면 모든 서버에 사용자의 ssh key를 추가하지 않고 git 같은 저장소에 저장하고 AuthorizedKeysCommand 에서 이 저장소로부터 key를 가져오게 하면 쉽게 중앙 집중 관리가 가능

Posted by 김형준

« Previous : 1 : 2 : 3 : 4 : 5 : ... 224 : Next »