ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Hands On] 아파치 레인저(Apache Ranger) 도커(Docker) 환경에서 하둡(Hadoop) 서비스(HDFS, Hive, Presto) 연동 테스트하기
    Data 2020. 12. 18. 22:47
    반응형

    | 이 글은 제가 작성한 원본(영어) 에 좀 더 현실적인 컨텍스트를 더해 작성하였습니다.

     

    데이터 플랫폼이 클라우드 환경이라면 제공되는 기본적인 서비스와 추가할 서비스의 연동은 어느 정도 보장 되고, 아직 규모가 많이 크지 않은 환경에서는 그런 부분이 큰 우선순위로 여겨지지 않기 때문에 비교적 데이터 플랫폼 운영자 입장에서 크게 신경을 쓰는 부분이 아닌 듯 합니다. 그러나, 어느 정도 규모가 있는 온프레미스 데이터 플랫폼을 운영하다보면, 데이터 사용자가 필요한 서비스나 도구를 추가할 때에 자주 '권한관리' 부분이 병목이 됩니다. 기존 운영하던 Apache Sentry와 인하우스의 조합으로는 Presto, Spark 등 끊임없이 개선되고 추가되는 다양한 데이터 시스템을 빠르게 연동하고 데이터 플랫폼에 안정적으로 편입시키는 데는 한계가 있었습니다. 

     

     이번 글에서는, 그러한 상황에서 해결책으로 자주 거론되는 Hadoop 환경의 권한관리 오픈소스 Apache Ranger를 Docker환경에서 Hadoop 서비스들(HDFS, Hive, Presto)와 같이 연동 테스트하며 POC(Proof of Concept)를 진행했던 부분을 공유드리려 합니다. 

     

     한 번은 Ranger 서비스를 위해서, 한 번은 Hadoop Cluster를 위해서 2번의 'docker-compose up'이 필요한데, 먼저 컨테이너 구조에 대해서 설명드리고, Hadoop의 각 서비스와의 연동을 테스트해보겠습니다.

     

    준비사항

    • Git
    • Docker (많은 메모리 세팅 - 대략 12 GB 이상 권장)
    • Apache Ranger와 Hadoop Stack에 대한 기본적인 이해

     

    docker compose up

    먼저, 아래 코드를 실행하셔서 docker 클러스터를 생성해줍니다. 

    git clone https://github.com/kadensungbincho/apache-ranger-docker-poc.git
    
    docker create network ranger-env
    
    cd apache-ranger-docker-poc/docker-composes/ranger
    docker-compose up -d
    
    cd ../hadoop
    docker-compose up -d

    아래에서는 각 docker-compose up 별로 어떤 컨테이너들이 생성되고 각 컨테이너 안의 어떤 서비스들이 어떻게 상호작용하는지 간단히 살펴보려고 합니다. 

     

    첫 번째 'docker-compose up'을 했을 때 발생하는 일

    아래와 같이 4개의 컨테이너가 생성됩니다:

    • ranger-admin: 이 컨테이너에서는 Ranger Security Admin 이 실행되며 다음과 같은 기능을 제공합니다: 웹 UI를 통한 1) Policy 관리, 2) Audit 로그 뷰, 3) Ranger User 관리 기능. 아래 구조에서 보시듯이, MySQL DB를 운영 데이터 저장을 위해 사용하고 ElasticSearch를 Audit 로그 저장을 위해 사용하고 있습니다. 
    • ranger-usersync: Ranger의 유저 synchronization 서비스로 아래의 구조에서는 Usersync 서비스가 동작하는 컨테이너의 Unix 계정을 Ranger 유저로 주입시켜 줍니다. 옅게 칠해진 부분과 같이 LDAP의 유저를 Ranger에 넣는 것도 가능하며, 여러 대의 Usersync 서비스를 실행하는 부분도 가능합니다.
    • ranger-db: MySQL이 실행되는 컨테이너
    • ranger-es: Agent가 존재하는 서비스로부터 바로 직접 받은 Audit 로그를 저장하기 위한 ElasticSearch가 실행되는 컨테이너

    첫 번째 'docker-compose up'을 했을 때 생성되는 컨테이너들

    첫 번째 'docker-compose up' 30 ~ 60초 이후에, http://localhost:6080 을 통해서 Web UI에 접근해 admin / rangeradmin1 을 통해 로그인하면, 아래와 같은 Service Manager 페이지를 확인하실 수 있습니다.

    로그인 랜딩 페이지

     

    두 번째 'docker-compose up'을 했을 때 발생하는 일

    두 번째 'docker-compose up'은 Ranger와 같이 연동테스트를 진행하기 위한 Hadoop 서비스 컨테이너들이 생성됩니다. 연동 테스트를 하는 주요 서비스인 HDFS, Hive, Presto에는 각각 Plugin(Agent)가 미리 설치되어 있습니다. 

    두 번째 'docker-compose up'을 했을 때 생성되는 컨테이너들

    위 이미지에서는 굵은 글씨로 된 3가지 부분이 있는데, 각 항목과 관련해서는 아래의 설명과 같습니다:

    • Plugin: 위에서 말씀드린대로 Apache Ranger와 연동되는 서비스들은 Plugin을 설치하고 환경설정을 해준 후 각 서비스를 실행시켜서 권한관리가 Ranger를 통해 진행되도록 하여야 합니다.
    • ES로 Audit 로그 송신: 각 Plugin은 각 서비스에 접근하는 요청에 대한 Audit 로그를 직접 ES로 송신합니다. 이 로그들은 Ranger Security Admin UI를 통해 확인할 수 있습니다.
    • Ranger Security Admin을 통한 Policy 체크 및 적용: plugin은 Ranger Security Admin을 체크하여 변경된 policies를 적용하고, 특정 서비스의 Resource에 특정 유저가 행한 요청이 허용될 수 있는지 policies를 확인하여 접근을 허용하거나 막습니다.
    728x90

    Hadoop 환경의 서비스는 어떻게 Ranger와 연동되는가?

    이제 위에서 생성한 Ranger, Hadoop 클러스터를 통해서 HDFS, Hive, Presto 각 서비스가 Ranger와 어떻게 연동되는지 테스트해보겠습니다. 

     

    HDFS

    HDFS에서의 Resource는 HDFS path에 해당되며 Ranger는 특정 유저가 특정 path에 행한 요청과 관련해 적절한 권한을 가지고 있는지 아래 2가지 단계를 통해 평가(evaluate)합니다.

    • 먼저 해당 resource와 유저에 적용된 policy가 있는지 확인하고, 존재하면 그것을 적용(아래 audit 로그에서 ranger-acl로 표기됩니다)하고 아니라면 2단계로 넘어갑니다. 
    • HDFS에 기본 권한관리 시스템에 일임합니다(hadoop-acl).

    그렇기에, 아래와 같이 다양한 케이스에서 접근이 허용되고 막힐 수 있습니다:

    • HDFS에 777인 path에 ranger policy가 없는 경우 user 'hive'의 read: 가능
    • HDFS에 000인 paht에 ranger policy가 user 'hive'에 대해 read, execute, write을 허용하는 경우 user 'hive'의 read: 가능

    등등 여러 권한 체계가 존재함과 동시에 Ranger policy의 경우 'recursive'가 기본적으로 true여서(/를 막으면 subpath는 모두 막힘) POC를 진행하며 운영 시에는, HDFS default umask를 700 로 바꾸는 것을 고민했었습니다. 

     

    위의 HDFS-Ranger 연동 기본사항에 대한 정보와 같이 아래에서 테스트를 위해, 먼저 HDFS plugin을 Ranger UI에서 등록하겠습니다.

    로그인 후 나타나는 랜딩페이지에서 'Access Manager' -> 'HDFS +'를 통해 이동 후, 아래와 같이 HDFS Configuration을 세팅해줍니다(Test Connection이 실패가 나오는 경우도 있는데, 일단 무시하고 Save하시면 됩니다):

    HDFS Configuration

    주요 항목과 관련해 짧게 설명을 드리면 다음과 같습니다:

    • Service Name: 이곳에서 설정해둔 서비스명입니다
    • Username and Password: HDFS plugin client가 Ranger admin에 접근 시 사용하는 계정입니다 (Ranger의 default 유저가 admin이고 비밀번호는 이곳에서 설정해두었습니다)
    • Namenode URI: 도커 설정에 따라 설정하시면 됩니다

    이후, 'Audit' -> 'Plugins' 화면에서 HDFS plugin이 제대로 등록되었는지 확인하실 수 있습니다:

    Plugin Audits

     

    HDFS 테스트1 - HDFS root path에 대한 모든 유저의 접근을 막기

     

    아래와 같이 'hdfs' -> 'Add New Policy'로 이동하여, 새로운 policy를 등록하여 접근을 막아보겠습니다.

     

    보이는 2개의 policy는 default입니다

    이후, 아래와 같이 policy를 설정합니다:

    Save를 눌러주고, 아래와 같이 'datanode' 컨테이너로 들어가 해당 path의 소유자가 아닌(현재 root가 소유자) user 'hive'로 HDFS path에 접근하려 해보겠습니다.

    # in your terminal
    docker exec -it datanode bash
    
    # in the 'datanode' container as root user
    useradd hive
    su hive
    
    # in the 'datanode' container as hive user
    /opt/hadoop-3.2.1/bin/hdfs dfs -ls /user/hive
    ls: Permission denied: user=hive, access=EXECUTE, inode="/user/hive"

     

    잘 막히는 부분을 확인할 수 있고, Ranger UI에서 Audit 로그도 확인할 수 있습니다:

    user 'hive'의 HDFS /user/hive path에 대한 Execute 접근이 ranger-acl을 통해 막힌 로그

     

    HDFS 테스트2 - user 'hive'에 대한 특정 HDFS path를 recursive하게 허용하기

    이제 default hive warehouse(서비스 Hive에서 테이블 생성 시, 기본적으로 생성되는 path) path인 /user/hive/warehouse  resource에 대하여 user 'hive'의 접근을 허용해 보겠습니다. 

     

    먼저, Ranger UI에서 user를 생성하고 policy를 생성해주겠습니다.

     

    'Settings' -> 'Users/Groups/Roles'로 이동하여 'Add New User'를 클릭하고, 아래와 같이 유저를 생성해줍니다.

    user 'hive' 생성하기

    그리고, 아래와 같이 policy를 생성해줍니다.

     

    /user/hive/warehouse* 에 대한 policy 생성

    위에서 주의할 점은 /user/hive/warehouse 로 생성하면 안된다는 부분입니다. 

     

    이제 해당 path에 아래와 같이 접근을 해보면, 

    # in your terminal
    docker exec -it datanode bash
    
    # in the 'datanode' container as root user
    su hive
    
    # in the 'datanode' container as hive user
    /opt/hadoop-3.2.1/bin/hdfs dfs -ls /user/hive/warehouse
    ls: Permission denied: user=hive, access=EXECUTE, inode="/user/hive/warehouse"

    안타깝게도 실패하는 것을 볼 수 있습니다. 이는 이전에 생성한 policy(blockRootAccess)가 priority(Deny가 Allow보다 우선함)가 있어 막히는 것입니다.

     

    위 상태에서의 Policy 리스트

    'blockRootAccess' policy를 삭제해주고 나서, 다시 접근하면 접근 가능한 것을 확인하실 수 있습니다. 

     

     

    Hive

    Hive plugin은 'Table', 'Database'와 같은 resource를 다룹니다. table의 데이터는 저장소(위의 구성에서는 HDFS)에 기반해서 존재하기 때문에, Hive 권한관리는 Hive resource에 대한 것과 실제 Hive가 물리적으로 데이터에 접근할 때 저장소의 권한관리 시스템도 적용받는 다는 부분이 위에서 살펴본 HDFS-Ranger와 다릅니다. 

     

    Hive는 또한 HDFS보다 더욱 많은 Access 레벨을 가지고 있으며 Ranger의 AccessType에서는 Hive 서비스 자체의 다양한 Access 레벨을 단순화하여(몇 가지를 하나의 AccessType으로 묶어서) 아래와 같이 mapping 해주고 있습니다:

    Hive - Ranger 권한 매핑 중 일부 - https://cwiki.apache.org/confluence/display/RANGER/Hive+Commands+to+Ranger+Permission+Mapping

    그러면, Hive 접근 패턴을 테스트해보겠습니다(보다 정확히는 Hive Metastore가 아닌, Hive Server에 대한 접근 관리입니다).

     

    Hive 테스트1 - Hive 접근을 모두 막아보기

     

    먼저 Hive plugin을 등록해주겠습니다. 'Access Manager' -> 'Hadoop SQL +'로 이동해서, 아래와 같이 폼을 채워줍니다(이 글을 작성하면서 알아차리게 되었는데, Hive와 Ranger 사용 시 Ranger Authorizer가 추가되기에 Zookeeper가 필요한 부분을 확인하고 추가해두었습니다).

     

    Hive plugin 등록하기 ( jdbc:hive2://hive-server:10000/;serviceDecoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2 )

     

    역시 Hive plugin이 잘 등록된 부분을 아래와 같이 확인하고,

     

     

    아래와 같이 beeline을 통해 user 'hive'로 Hive Server에 접근해서 user 'hive'가 database나 table에 접근할 수 있는지 확인할 수 있습니다.

     

    # in your terminal
    docker exec -it hive-server bash
    
    # in the 'hive-server' container as root user
    /opt/hive/bin/beeline
    
    # in the beeline console
    !connect jdbc:hive2://127.0.0.1:10000 hive hivehive1(or type the password you put when you create hive user)

     

    안타깝게도 아래와 같이 에러가 발생하는 것을 확인할 수 있는데요.

     

    20/12/17 12:56:46 [main]: WARN jdbc.HiveConnection: Failed to 
    connect to localhost:10000
    Error: Could not open client transport with JDBC Uri: j
    dbc:hive2://localhost:10000: Failed to open new session: 
    java.lang.RuntimeException: 
    org.apache.hadoop.ipc.RemoteException(org.apache.ranger.authorizatio
    n.hadoop.exceptions.RangerAccessControlException): Permission 
    denied: user=hive, access=EXECUTE, inode="/tmp/hive" at 
    org.apache.ranger.authorization.hadoop.RangerHdfsAuthorizer$RangerAc
    cessControlEnforcer.checkPermission(RangerHdfsAuthorizer.java:466)

     

    Hive Server가 로그인한 user 'hive'를 위해서 HDFS 저장소의 /tmp/hive에 user 'hive'(Hive Server에 doAs를 걸어두어서)로 write을 하려다 실패하였다고 나옵니다. 그렇기에 user 'hive'에 대하여 /tmp/hive에 write할 수 있도록 HDFS policy를 설정하여야 합니다. 현재로는 위에서 생성한 'blockRootAccess'가(테스트를 위해 다시 생성했었습니다) 존재하여서 오류가 발생했기에, 그 policy를 삭제하고 다시 접근하면 아래와 같이 성공하는 것을 볼 수 있습니다.

     

    !connect jdbc:hive2://127.0.0.1:10000 hive hivehive1(or type the password you put 
    when you create hive user)
    
    Connecting to jdbc:hive2://127.0.0.1:10000
    Connected to: Apache Hive (version 3.1.2)
    Driver: Hive JDBC (version 3.1.2)
    Transaction isolation: TRANSACTION_REPEATABLE_READ
    0: jdbc:hive2://127.0.0.1:10000> show databases;
    
    ...
    
    INFO  : OK
    INFO  : Concurrency mode is disabled, not creating a lock manager
    DEBUG : Shutting down query show databases
    
    +----------------+
    | database_name  |
    +----------------+
    | default        |
    +----------------+
    
    1 row selected (1.072 seconds)

     

    이제, 'all-databases, tables, columns' default policy를 수정해서, user 'hive'에 대한 Deny 조건을 더하여 user 'hive'의 모든 access를 막아보겠습니다:

     

    all - databases, tables, columns 수정
    user 'hive'에 대한 Deny 조건 추가

     

    위와 동일하게 beeline 콘솔에서 확인 시, 잘 막히는 부분을 확인할 수 있습니다.

     

    0: jdbc:hive2://127.0.0.1:10000> show databases;
    
    Error: Error while compiling statement: FAILED: HiveAccessControlException 
    Permission denied: user [hive] does not have [USE] privilege on [Unknown resource!!] 
    (state=42000,code=40000)

     

    마무리

    개인적으로 새로운 권한관리 도구인 Apache Ranger POC를 진행하면서, HDFS, Hive와 같은 Hadoop 환경의 서비스를 더욱 잘 이해할 수 있게 되었습니다. 그러한 이유는 사실 Ranger가 문서화가 잘 되어 있지 않아서, 많은 부분 코드를 살펴보게 되었고 Ranger가 위의 서비스들과 꽤나 연관성이 깊어서 그런 것 같았습니다 (또한, 코드양이 HDFS나 Hive 보다는 적어서 진입점?으로도 좋은 듯 합니다).

     

    이 글이 같은 고민을 하시는 분들에게 도움이 되었으면 좋겠습니다. 

    반응형
Kaden Sungbin Cho