샤딩 (Sharding) 을 하는 방법 또는 복제 셋 (Replication) 을 하는 방법에 관한 예제는 잘 나와 있습니다. 그런데 실전에서는 복제와 샤딩을 동시에 하는 것이 일반적일 것입니다. 이것에 관한 예제가  많이 없더군요. 어쩌다 찾은 것이 한개 있지만 한 기계에서 가상으로 돌려보는 예제 입니다. 이러한 예제로는 샤딩을 제대로 테스트 할 수가 없더군요. 하지만 그 문서를 바탕으로 실제로 샤딩과 복제를 클러스터 환경에서 테스트 해 봤습니다. 

http://cookbook.mongodb.org/operations/convert-replica-set-to-replicated-shard-cluster/ 

위 내용은 한 기계안에서 샤딩과 복제 셋을 테스트 하는 예제 입니다. 


원칙대로라면 복제 셋 (Replication Set) 을 설정할 때 한 머신에서 동작시키는 것이 아니라 서로 다른  서버에서 구동되게 설계를 해야 하지만 그것 까지 세팅하기가 매우 귀찮기 때문에 다음 과 같이 설계를 해 봤습니다. 

   

    Machine 1 : 

    - name : super1
    - replication : first * 3


    Machine 2 :

    - name : super2
    - replication : second * 3

    


 

 즉 2대의 머신에서 샤딩이 이루어 지게 세팅을 하는 것인데, 각각의 머신은 3개의 복제 셋을 가지게 구성 되는 것입니다. 

mongoDB 의 bin 이 path 에 걸려 있다는 가정 하에 진행 합니다. 

super1 부터 설정을 시작합니다.
  먼저 데이터들이 저장될 db 디렉토리를 만들어 줍니다. 

   

    $ mkdir ~/db/first1 
    $ mkdir ~/db/first2
    $ mkdir ~/db/first3

    
    로그를 저장할 공간도 만들어 줍니다. 

    $ mkdir ~/db/log 


    이제 몽고 데몬들을 띄워줍니다. 

 

    $ mongod --dbpath ~/db/first1 --port 10001 --replSet first > ~/db/log/first1.log &
    $ mongod --dbpath ~/db/first2 --port 10002 --replSet first > ~/db/log/first2.log &
    $ mongod --dbpath ~/db/first3 --port 10003 --replSet first > ~/db/log/first3.log &


    이제 이 세개의 몽고 디비 프로세스들을 한개의 복제셋 (여기서는 first )으로 묶어주는 작업을 합니다. 

 

  $ mongo localhost:10001/admin 

    > db.runCommand({"replSetInitiate" : {"_id" : "first", "members" : [{"_id" : 1, "host" : "super1:10001"}, {"_id" : 2, "host" : "super1:10002"}, {"_id" : 3, "host" : "super1:10003"}]}})
    {
        "info" : "Config now saved locally.  Should come online in about a minute.",
"ok" : 1
    }


    이러면 성공적으로 복제 셋이 만들어 진것 입니다. 이제 데이타를 실제로 넣어보기로 하겠습니다. 

   

PRIMARY> use test
    switched to db test
    PRIMARY> people = ["Marc", "Bill", "George", "Eliot", "Matt", "Trey", "Tracy", "Greg", "Steve", "Kristina", "Katie", "Jeff"];
    PRIMARY> for(var i=0; i<1000000; i++){
          name = people[Math.floor(Math.random()*people.length)];
 user_id = i;
 boolean = [true, false][Math.floor(Math.random()*2)];
 added_at = new Date();
 number = Math.floor(Math.random()*10001);
 db.test_collection.save({"name":name, "user_id":user_id, "boolean": boolean, "added_at":added_at, "number":number });
    }

 
    이름을 백만건 정도 랜덤으로 순서를 매겨서 집어넣는 소스코드 입니다. 


이제는 구성된 복제 셋을 가지고 샤딩으로 추가해 주는 작업을 해 줘야 할 차례 입니다. 역시  super1  서버에서 작업을 해 줍니다. 

config 서버를 띄워줄 차례 입니다. 먼저 config 데이타들이 저장 될 디렉토리를 만들어 줘야 합니다. 

   $ mkdir ~/db/config1
   $ mkdir ~/db/config2
   $ mkdir ~/db/config3


컨피그 서버는 1대 아니면 3대를 추천합니다. (제가 아니라 몽고디비 쪽이 추천하는 것입니다) 

 

   $ mongod --configsvr --dbpath ~/db/config1 --port 20001 > ~/db/log/config1.log &
   $ mongod --configsvr --dbpath ~/db/config2 --port 20002 > ~/db/log/config2.log &
   $ mongod --configsvr --dbpath ~/db/config3 --port 20003 > ~/db/log/config3.log &


혹시 컨피그 서버가 제대로 띄워지지 않으면 다른 포트로 띄워주시면 됩니다. 이제 컨피그 서버에서 정보를 가져오는 mongos (라우터 개념)를 띄워줄 차례 입니다. 

 

 $ mongos --configdb super1:20001,super2:20002,super3:20003 --chunkSize 1 > ~/db/log/mongos.log &


 chunkSize 는 테스트에서만 추가하는 것으로 생각하시면 됩니다. 크기를 1메가로 만드는  것입니다. (기본은 64메가 입니다) 이제 띄워논 mongos 에 접속해야 한다. 

 

 $ mongo super1/admin
  
 mongos> db.runCommand( { addshard : "first/super1:10001,super2:10002,super3:10003" } )

   { "shardAdded" : "first", "ok" : 1 }
   mongos>
   
  
이제 두번째 기계에서 세팅을 할 차례 입니다. super2 (Machine 2) 입니다.  먼저 데이타베이스가 저장될 디렉토리를 만들어 줍니다. 

   

   $ mkdir ~/db/second1 
   $ mkdir ~/db/second2
   $ mkdir ~/db/second3 
   

로그용 디렉토리도 만들어 줍니다. 
   

$ mkdir ~/db/log 


몽고디비 데몬을 띄워줍니다. 

   

   $ mongod --dbpath ~/db/second1 --port 10001 --replSet second > ~/db/log/second1.log &
   $ mongod --dbpath ~/db/second2 --port 10002 --replSet second > ~/db/log/second2.log &
   $ mongod --dbpath ~/db/second3 --port 10003 --replSet second > ~/db/log/second3.log &


이제 이 프로세스들을 한 개의 복제 셋(replication set)으로 묶어 줍니다. 

 

 $ mongo super2:10001/admin

   > db.runCommand({"replSetInitiate" : {"_id" : "second", "members" : [{"_id" : 1, "host" : "super2:10001"}, {"_id" : 2, "host" : "super2:10002"}, {"_id" : 3, "host" : "super2:10003"}]}})
   {
      "info" : "Config now saved locally.  Should come online in about a minute.",
      "ok" : 1
   }



방금 만들어 진 복제 셋 (replication set) 을 새롭게 샤드 (shard) 에 추가해 줍니다. 먼저 mongos 를 띄워줍니다. 지금 super2 서버고 mongos 도 super2 에서 띄우는 것이지만 바라보는 컨피그 서버 는 super1 에서 띄운 것을 쳐다봐야 합니다. 

   $ mongos --configdb super1:20001,super2:20002,super3:20003 --chunkSize 1 >  ~/db/log/mongos.log &


몽고 shell 을 이용해서 mongos (이건 super2 에 띄워 놓은 것입니다) 에 접속합니다. 

   

$ mongo super2/admin
   mongos> use admin
   switched to db admin
   mongos> db.runCommand( { addshard : "second/super2:10001,super2:10002,super3:10003" } )
     { "shardAdded" : "secondset", "ok" : 1 }


이러면 두번째 복제 셋 (replication set) 을 샤드에 추가 해 주었습니다.  제대로 되어 있는지 테스트를 확인 해 보기로 합니다. 

 

 mongos> db.runCommand({listshards:1})
   {
        "shards" : [
  {
      "_id" : "first",
      "host" : "first/super1:10001,super1:10003,super1:10002"
  },
  {
      "_id" : "second",
      "host" : "second/super2:10001,super2:10002,super2:10003"
  }
     ],
     "ok" : 1
   }


샤드로 설정되어 있는 것하고 샤드를 직접적으로 하는 것은 차이가 있습니다. 실제로 샤드를 구현해 보겠습니다. mongos 에 접속되어 있는 상태에서 

 

 mongos> db.runCommand( { enablesharding : "test" } )
   { "ok" : 1 }


 
위 처럼 입력해 주면 'test' db 를 샤딩하겠다고 정해주는 것입니다. 이제 샤딩 키 (Sharding Key) 를 정해줘야 합니다. 샤딩 키를 바탕으로 (이 키로 정렬한다는 뜻입니다) 샤드들이 나눠지게 되기 때문에 샤딩키를 적절하게 정해주는 것은 아주 중요합니다. 인덱스를 정해준다고 보셔도 무방합니다. 

 

   mongos> use test
   switched to db test
   mongos> db.test_collection.ensureIndex({number:1})

   
   생각 난 김에 인덱스도 정해줍니다. test 디비에 test_collection 이라는 컬렉션에서 number 라는 컬럼을
   인덱스로 지정해 주라는 명령입니다. 

   

mongos> use admin
   switched to db admin
   mongos> db.runCommand( { shardcollection : "test.test_collection", key : {"number":1} })
   { "collectionsharded" : "test.test_collection", "ok" : 1 }
   mongos>


   
test 디비에 test_collection 이라는 컬렉션에서 number 라는 컬럼을 샤딩키로 해서 샤딩을 해 주라는 명령입니다. 

제대로 됐는지 확인을 해 보겠습니다. 

 

   mongos> use test
   switched to db test
   mongos> db.stats()


   
db.stats() 나 db.printShardingStatus() 로 확인하시면 됩니다. 



화살표는 신경 쓰지 맙시다..

Consistency : 일관성
  각각의 사용자가 항상 동일한 데이터를 조회한다.

Availability : 가용성
  모든 사용자가 항상 읽고 쓸 수 있다.

Partition Tolerance : 확장 가능성
  물리적 네트워크 분산 환경에서 시스템이 잘 동작한다.


기존의 RDBMS 로 칭해지는 데이타베이스들은 CA 에 취중합니다. 따라서 확장이 용이하지가 않고, 대신 최근 트렌드가 되고 있는 NoSQL 들은 기본적으로 P 의 성능이 좋습니다. 그래서 확장성은 기본입니다. 대신 C A 의 일부분을 희생합니다.

카산드라 (Cassandra) 는 AP 를 추구 하고, HBase 는 CP 를 추구합니다.

카산드라는 페이스북이 채택하고 개발해진 것으로 알려지고 유명해 졌는데요. Consistency 모델이 페이스북의 새로운 메시지 플랫폼을 구현하기에는 어렵다고 판단이 되서 , 새로운 메시지 플랫폼은 HBase 로 구현했다고 합니다. 즉 AP 대신 CP 로 페이스북의 데이타의 중요성에 대한 잣대가 이동한 것이지요.






어디가서 기술 모른다고 기 죽지 말고 들어본 것처럼 말 할 필요가 있는 매니져분들을 위한 1분 짜리 하둡 정리 입니다.더 복잡하고 더 깊이 있는 내용은 '공부'를 해야 하기 때문에 제가 내세우는 취지와는 안 맞을 것입니다. 

하둡의 화두는 '분산' 입니다. 

하둡은 두가지 큰 요소의 결합입니다. '처리(계산)' 와 '저장' 입니다. 즉 '분산처리' 와 '분산저장' 이라고 보면 됩니다. 여러개의 저가형 컴퓨터를 마치 하나인것 처럼 묶어주는 기술이라고 보면 됩니다. (그래서 계산 능력과 저장 공간을 늘립니다)

분산저장

하둡 파일시스템(HDFS: Hadoop Distributed File System) 을 이용해서 파일을 적당한 블록 사이즈 (64MB)로 나눠서 각 노드 클러스터(각각의 개별 컴퓨터) 에 저장합니다. 또한 데이타 유실의 위험이나 사람들이 많이 접근할때 (Access) 할때의 부하처리를 위해서 각 블록의 복사본 (Replication)을 만들어 둡니다. 보통 복사본은 최소 3카피 정도입니다. 왜 이렇게 하냐면 고성능 서버에서 저장공간은 돈이 많이 들어가는 부분입니다. 그것을 저가형 저장소 여러개를 묶어서 마치 레이드 처럼 동작하게 하려는 목적으로 분산저장을 하는것입니다. 

분산처리

MapReduce 라는 프레임워크를 이용해서 계산합니다. 프레임워크라는 것이 중요합니다. 즉 분산처리를 위해서 프레임워크를 만들어 둔것입니다. 분산처리를 위해서 프레임워크에 맞추어서 코딩을 하고 하둡 시스템에서 그것을 실행하면 자동으로 분산처리를 해 줍니다. 
   
그 맵리듀스 (MapReduce) 라는 프레임 워크는 Map + Reduce 라는 두가지 형식으로 나누어 집니다. 개념은 간단합니다. Map 함수에서 데이타를 처리를 하고 Reduce 함수에서 원하는 결과값을 계산시킵니다. 

여러가지 설명이 있겠지만 정말 쉽게 설명을 하자면 하둡은 분산을 지원하기 위한 자바를 이용한 '가상 OS' 와 같은 개념으로 보면 가장 편리할 것입니다. (보통 OS 가 파일 시스템 과 잡 스케쥴러를 기반으로 구성됩니다)





+ Recent posts