RMP 연습

Realm 모바일 데이터베이스와 Realm 오브젝트를 포함한 전체 Realm 플랫폼을 시작하는 것은 어려워 보일 수 있지만 실제로는 간단합니다. 이 글에서는 Realm의 AMI나 Ubuntu Linux 인스턴스를 사용해서 아마존 웹 서비스(AWS)에 Realm 오브젝트 서버를 다운로드, 설치, 설정 및 설치하는 방법을 다룹니다. 그런 다음 다중 사용자 지원을 위해 Realm 오브젝트 서버를 사용하는 작은 iOS 애플리케이션을 빌드하는 방법을 알려 드립니다. 그리고 Realm Functions로 “서버리스” 리액티브 서버사이드 프로그래밍을 하는 방법을 안내합니다.

다음으로 프로페셔널 에디션으로 업그레이드하는 방법과 프로페셔널 에디션의 필요 이유를 설명한 후 이벤트 핸들링으로 서버사이드 프로그래밍을 하는 다른 방법을 알려드립니다. 마지막으로 동기 백업과 수동 장애 조치를 포함한 엔터프라이즈 에디션 기능을 소개합니다.

Realm 플랫폼 개요

Realm 플랫폼의 핵심 컨셉은 Realm 이라 부르는 가벼운 객체 컨테이너입니다. 일반 데이터베이스처럼 Realm 내의 데이터는 쿼리와 필터링, 상호 연결, 저장이 가능합니다. 하지만 기존 데이터베이스와는 달리 Realm의 객체는 라이브이고 온전히 반응형입니다. Realm은 매끄럽게 기기와 애플리케이션 사이에서 동기화를 해주며 스레드 사이에서 안전하게 접근할 수도 있습니다.

Realm 모바일 데이터베이스 는 모바일 기기를 위해 기초부터 구축되었습니다. 기존 데이터베이스와 달리, Realm 내의 객체는 _네이티브 객체_입니다. 데이터베이스 밖으로 객체를 복사해서 수정하고 다시 저장할 필요가 없으므로 항상 “라이브” 상태의 실제 객체를 작업할 수 있습니다. 한 스레드나 프로세스가 어떤 객체를 수정하면 다른 스레드나 프로세스는 즉각 알림을 받을 수 있습니다. 객체의 동기화가 항상 유지되는 것이죠.

Realm 플랫폼으로 구축한 애플리케이션은 Realm 오브젝트 서버 상에서 Realm에 접근하거나 Realm을 생성할 수 있습니다. 애플리케이션은 오직 Realm 오브젝트 서버로의 연결 정보와 서버사이드 Realm의 URL만 알면 됩니다. Realm 오브젝트 서버의 객체는 애플리케이션의 요구에 따라 다운로드되고 Realm의 로컬 복사본이 오브젝트 서버의 복사본과 완벽하게 동기화된 상태로 유지됩니다.

Realm 플랫폼의 핵심 컨셉은 다중 Realm을 사용하는 것입니다. 애플리케이션이 단일 데이터 소스에 국한되지 않고 데이터 소스는 웹 페이지처럼 쉽게 접근할 수 있습니다. Realm은 사용자 계정에 따른 정밀한 접근 제어도 제공합니다. Realm 오브젝트 서버의 Realm은 모든 사용자가 사용할 수 있는 공용 이거나 생성한 사용자만이 접근할 수 있는 개인 타입일 수도 있으며, 특정 유저 사이에 공유 되는 타입일 수도 있습니다. 공용 Realm이 온라인 상점의 카탈로그라면 개인 Realm은 사적인 할 일 리스트나 앱 설정, 공유 Realm은 어떤 가족의 쇼핑 리스트일 수 있습니다.

AWS에 Realm 개발자 에디션 설치

본 튜토리얼에서 AWS만을 다루지만, Azure Marketplace나 Digital Ocean, 자신의 Linux나 macOS 서버에도 Realm 오브젝트 서버를 설치할 수 있습니다. 자세한 내용은 Realm 오브젝트 서버 문서를 참고하세요!

Ubuntu 16.04 기반의 hardware virtual machine (HVM) 가상화를 사용한 Realm 플랫폼을 위한 공개 Amazon Machine Images (AMIs)를 제공합니다. 이미 Ubuntu나 Red Hat Enterprise, CentOS AMI를 실행하고 있다면 Realm의 PackageCloud 저장소에 있는 Realm 개발자 에디션을 설치할 수 있습니다.

개발자 에디션 AMI 설치

EC2 콘솔에서 사이드바의 Images 아래에 있는 AMIs 를 클릭하고 원하는 지역에 따라 다음 중 하나의 이미지를 검색하세요.

Region Image AMI ID
ap-northeast-1 (Tokyo) ami-a03233c7
eu-west-1 (Ireland) ami-aecfdec8
us-east-1 (N. Virginia) ami-8a62289c

이미지를 선택하고 Launch 를 클릭합니다. 다음 화면의 “Choose an Instance Type”에서 인스턴스 타입을 선택합니다. 기본 값 t2.micro 그대로 놔둬도 됩니다. 프리 티어를 그대로 둬도 괜찮지만 m3.medium를 권장합니다. 이제 Review and Launch 를 클릭합니다.

마지막으로 Launch 를 클릭합니다. SSH 접근이나 새로운 SSH 생성을 위해 기존 공개/개인 키 쌍을 선택하라는 메시지가 표시됩니다. 인스턴스가 시작되면 View Instance 를 클릭하세요. 이제 대시보드에서 실행 중인 인스턴스와 공개 IP 주소를 볼 수 있습니다.

Realm 인스턴스에는 22 포트(SSH)가 기본으로 열려 있지만 오브젝트 서버에 특화된 포트는 열려 있지 않습니다. 따라서 Realm 대시보드와 클라이언트 접근을 위한 9080 포트와 동기화 워커를 위한 7800 포트, 백업 워커를 위한 27810 포트를 열어야 합니다. 또한, 모니터링에 statsd를 사용한다면 8125 포트도 열어야 합니다. 인스턴스의 보안 그룹을 클릭하고 Edit inbound rules 를 클릭하세요. 9080 포트를 위한 커스텀 TCP 규칙에는 모든 IP 허용을 위해 0.0.0.0/0 허용을 설정하거나, 22-9999처럼 연결을 제한할 IP 주소 범위를 설정합니다. 단, 특정 포트를 여는 것보다는 보안성이 떨어집니다.

규칙을 설정하면 브라우저에서 <ip-address>:9080에 방문해서 Realm 대시보드를 시작할 수 있습니다.

기존 Linux AMI 인스턴스에 설치

Realm은 PackageCloud라는 서비스를 통해 패키지 저장소를 관리합니다. 간단한 몇 단계만 거치면 Realm 오브젝트 서버를 설치하고 시작할 수 있습니다.

주의: 이 안내와 튜토리얼은 자신의 서버에서 Ubuntu Linux를 실행하고 있다고 가정합니다. RHEL나 CentOS에서 yum 패키지 관리자를 사용합니다. 자세한 내용은 Linux 설치 가이드를 참고하세요.

  1. 기존 인스턴스에 SSH 접근

  2. Realm PackageCloud 저장소에서 Realm 오브젝트 서버 설치. Ubuntu에서는 다음 명령어를 입력합니다.

    curl -s https://packagecloud.io/install/repositories/realm/realm/script.deb.sh | sudo bash
     sudo apt-get update
     sudo apt-get install realm-object-server-developer

    RHEL/CentOS에서는 다음 명령어를 입력합니다.

    curl -s https://packagecloud.io/install/repositories/realm/realm/script.rpm.sh | sudo bash
     sudo yum -y install realm-object-server-developer
  3. Realm 오브젝트 서버 시작. RHEL/CentOS 6 버전에서는 다음 명령어를 입력합니다.

    sudo chkconfig realm-object-server on
     sudo service realm-object-server start

    Ubuntu나 RHEL/CentOS 7 버전에서는 다음 명령어를 입력합니다.

    sudo systemctl enable realm-object-server
     sudo systemctl start realm-object-server

마지막으로 Realm 클라이언트와 대시보드가 사용하는 AMI 인스턴스의 9080 포트가 열려 있는지 확인합니다. 동기화 워커를 위한 7800 포트, 백업 워커를 위한 27810 포트도 열어야 합니다. 또한, 모니터링에 statsd를 사용한다면 8125 포트도 열어야 합니다. 인스턴스의 보안 그룹을 클릭하고 Edit inbound rules 를 클릭하세요. 9080 포트를 위한 커스텀 TCP 규칙에는 모든 IP 허용을 위해 0.0.0.0/0 허용을 설정하거나, 22-9999처럼 연결을 제한할 IP 주소 범위를 설정합니다. 단, 특정 포트를 여는 것보다는 보안성이 떨어집니다.

모든 포트들이 열렸으면 브라우저에서 <ip-address>:9080으로 이동하고 Realm 대시보드를 시작합니다.

Realm 대시보드

AMI를 설정했으면 브라우저에서 <ip-address>:9080을 입력하고 대시보드로 이동합니다. 첫 접근이라면 이메일 주소와 암호를 넣어서 어드민 사용자를 생성하라는 메시지가 표시됩니다.

대시보드는 다음 기능을 제공합니다.

  • 대시보드: 입/출력 데이터 속도, 입/출력 바이트, 활성화된 연결, 활성화된 Realm 등의 시스템 상태
  • Realm: 해당 오브젝트 서버에 동기화되는 Realm의 경로, 권한, 소유 사용자를 드릴 다운 방식으로 보여주고 개별 Realm의 모델과 컨텐츠 표시
  • 사용자: 해당 오브젝트 서버의 사용자 정보와 관리, 어드민 권한 부여 및 삭제
  • Function: Realm Functions 관리 및 접근
  • Log: 선택해서 세부 내용을 볼 수 있는 오브젝트 서버의 시스템 로그

RealmLoginKit으로 iOS 앱 구축

다중 사용자 할 일 튜토리얼은 Realm 정식 예제 앱인 RealmTasks를 다중 사용자 버전으로 만드는 방법을 소개합니다. 튜토리얼은 다음 내용을 안내합니다.

  • Cocoapods을 사용해서 새 Realm 기반 프로젝트를 처음부터 설정하는 방법
  • 쉽게 다중 사용자를 위한 애플리케이션을 거의 코딩 없이도 생성해주는 무료 Realm 유틸리티 모듈인 RealmLoginKit을 적용하고 설치하는 방법
  • Realm 플랫폼 배포에 제공되는 모든 기능을 갖춘 RealmTasks와 상호 작용하는 간단한 Realm 기반 할 일 관리 앱을 만드는 방법

튜토리얼을 따르려면 macOS 10.12 이상의 매킨토시와 Xcode 8.2.3 이상 버전이 필요합니다.

튜토리얼의 GitHub에서 전체 안내와 소스 코드를 볼 수 있습니다.

https://github.com/realm-demos/realm-MultiUserTasksTutorial

Realm Functions 사용

이제 Realm 오브젝트 서버를 설정했으니 Realm Functions로 커스텀 서버사이드 기능을 작성할 수 있습니다.

먼저, 위에서 사용한 IP 주소로 웹 대시보드를 엽니다. 다음으로 왼쪽의 Functions 버튼을 클릭합니다. 그러면 모든 Functions 리스트를 볼 수 있습니다. “Create new Function”를 눌러서 새로 하나 만들면 다음 화면이 보입니다.

첫 필드는 Function의 제목입니다. 앞으로 생성할 여러 Function을 구분할 수 있는 파일 이름이라고 생각하면 됩니다.

다음은 어떤 Realm의 변경 사항을 모니터링할지 지정하는 필드입니다. Realm은 파일시스템과 비슷하게 구성되므로 이를 경로로 사용합니다. 정규 표현식 역시 지원하므로 특정 범주의 여러 Realm을 관찰하는 Function을 만들 수 있습니다.

튜토리얼에서는 다음 정규 표현식을 사용합니다.

^/([0-9a-f]+)/realmtasks$

이 경우 realmtasks라는 이름의 사용자 하위 디렉터리의 변경 사항을 감시합니다. 첫 번째 정규식 패턴인 ([0-9a-f]+)는 특정 유저에 속한 디렉터리를 찾는 방법으로 UUID에 따라 명명됩니다. 이 패턴을 사용해서 특정 사용자의 소유 여부와 상관없이 모든 realmtasks Realm을 볼 수 있습니다.

위 정규 표현식을 붙여넣었다면 다음으로 해당 Realm에서 생성된 새 변경 이벤트 각각에 반응할 Functions JavaScript 코드를 넣을 차례입니다. 아래 코드를 복사해서 코드 에디터에 붙입니다.

var Wit = require("node-wit").Wit;
var WIT_ACCESS_TOKEN = ""; // API TOKEN으로 업데이트
var witClient = new Wit({accessToken: WIT_ACCESS_TOKEN});

module.exports = function(change_event) {
    var realm = change_event.realm;
    var changes = change_event.changes.Task;
    var taskIndexes = changes.modifications;
    console.log("Change detected: " + changes);
    // 할 일 객체를 프로세스로 가져오기
    var tasks = realm.objects("Task");
    for (var i = 0; i < taskIndexes.length; i++) {
        var taskIndex = taskIndexes[i];
        // 인덱스를 사용해서 Realm으로부터 할 일 객체 가져오기
        var task = tasks[taskIndex];
        console.log("New task received: " + change_event.path);
        // wit.ai에서 date 가져오기
        // 다음 주소 사용 https://wit.ai/docs/http/20160526#get--message-link
        // node-wit: https://github.com/wit-ai/node-wit
        witClient.message(task.text, {}).then((data) => {
            console.log("Response received from wit: " + JSON.stringify(data));
            var dateTime = data.entities.datetime[0];
            if (!dateTime) {
                console.log("Couldn't find a date.");
                return;
            }
            console.log("Isolated calculated date: " + dateTime.value);
            // 날짜를 쓰려면 클라이언트에 날짜 속성을 추가하고 마이그레이션해야 합니다.
            realm.write(function() {
                task.date = new Date(dateTime.value);
            });
        })
        .catch(console.error);
    }
};

이 코드의 구조는 상당히 간단합니다. 관찰하던 Realm에 일련의 변경 사항이 Realm 오브젝트 서버에 동기화될 때마다 이 Function이 changeEvent 객체를 받습니다. 여기에는 변경된 객체의 인덱스가 포함되며, 추가, 수정, 삭제 여부도 알 수 있습니다. 하지만 실제 객체가 아닌 변경 객체의 인덱스만을 포함하므로 객체를 가져와야 합니다.

...
var realm = change_event.realm;
var changes = change_event.changes.Task; // Task 모델을 위한 change
var taskIndexes = changes.modifications; // 생성, 삭제는 제외하고 수정만을 확인
// 할 일 객체를 프로세스로 가져오기
var tasks = realm.objects("Task"); // 해당 모델 타입의 전체 객체 리스트
for (var i = 0; i < taskIndexes.length; i++) {
    var taskIndex = taskIndexes[i]; // 현재 인덱스
    // 인덱스를 사용해서 Realm으로부터 할 일 객체 가져오기
    var task = tasks[taskIndex]; // Realm에서 할 일 객체 가져오기
}
...

코드를 실행하기 전에 Wit.ai 접근 토큰을 얻기 위해 이 안내를 따르고], 할 일 객체의 텍스트에 포함된 datetime으로 date를 만들어 주는 Wit.ai 인스턴스를 설정합니다. 위 링크의 3단계까지 수행하면 Function의 윗부분에 필요한 접근 토큰을 받아서 WIT_ACCESS_TOKEN 변수에 설정할 수 있습니다.

마지막으로 node-wit 패키지를 설치해야 합니다. 셸을 열고 PATH에 자신의 Realm 플랫폼 폴더를 추가하세요.

cd ENTER/THE/PATH/TO/YOUR/RMP/FOLDER
PATH=.prefix/bin:$PATH npm install node-wit

접근 토큰을 넣고 패키지를 설치했으면 function을 저장하고 실행해야 합니다. 우상단의 ‘play’ 버튼을 누르면 좌측에 Enabled 표시가 보입니다.

Function의 동작 내용을 파악하고 디버깅하려면 페이지를 아래로 스크롤해서 콘솔을 확인하세요.

이제 iPhone 앱으로 돌아가서 “Buy groceries at 5pm tomorrow”나 “Go for a run in 20 minutes”와 같은 할 일을 넣으면, Function의 처리에 따라 시간이 나옵니다. 콘솔에 로그인해서 앱에 자동으로 업데이트되는 reminder 속성으로 처리한 후 할 일 아래에 텍스트로 표시해줍니다.

첫 번째 Function을 완성했습니다! 여기서 만든 reminder 속성으로 이들 할 일에 예약된 푸시 알림을 다음 Function에서 만들 수도 있겠네요. JavaScript로 원하는 대로 서버사이드 기능을 구축할 수 있습니다.

프로페셔널 에디션으로 업그레이드

왜 업그레이드를 해야 할까요? 모든 Realm 플랫폼 사용자는 Realm Functions를 사용할 수 있고, 프로페셔널이나 엔터프라이즈 에디션 사용자라면 Realm 오브젝트 서버 외부에서 Node.js을 위한 Realm 동기화 바인딩을 사용할 수 있으며, 이벤트 핸들링과 데이터 접근, 데이터 커넥터 기능 역시 사용할 수 있습니다.

따라서 이 기능을 데이터 변경에 더욱 효율적으로 반응하는 분산 이벤트 핸들링에 사용하거나, 기존 서버와 Realm 오브젝터 서버가 상호 작용할 수 있도록 해서 Realm 오브젝터 서버가 단순히 들어온 데이터에만 반응하는 것이 아니라, Realm 오브젝트 서버로 데이터를 보낼 수도 있습니다.

Realm 플랫폼 프로페셔널 에디션 무료 14일 평가판을 사용하려면 이 양식을 사용하세요. 이메일로 접근 토큰을 보내 드리겠습니다.

접근 토큰을 얻었다면 다음으로 진행합니다. 먼저 Realm의 데이터를 그대로 유지한 채로 개발자 에디션을 중지하고 언인스톨한 후 프로페셔널 에디션을 설치합니다. 이 작업은 서버상의 Unix 셸 프롬프트에서 수행할 수 있습니다.

Realm 오브젝트 서버를 실행하는 서버에 SSH 접속하고 다음 명령어를 입력하세요. (#로 시작하는 주석은 입력하지 않아도 됩니다)

# 언인스톨 전 서비스 중지
sudo systemctl stop realm-object-server

# 개발자 에디션 언인스톨
sudo apt-get remove realm-object-server-developer

# 새로운 프로페셔널 에디션 토큰 설정
export PACKAGECLOUD_TOKEN=<the token you received via email>

# 새로운 패키지 저장소 설정
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-professional/script.deb.sh | sudo bash
sudo apt-get update

# Realm 오브젝트 서버 설치
sudo apt-get install realm-object-server-professional

# 서비스 활성화 및 시작
sudo systemctl enable realm-object-server
sudo systemctl start realm-object-server

같은 IP 주소, 포트, 어드민 계정으로 Realm 오브젝트 서버 대시보드에 접근해서 이전 섹션에서 생성한 Realm을 볼 수 있어야 합니다.

이벤트 핸들러 생성

Realm 이벤트 핸들링을 사용하려면 작은 Node.js 애플리케이션을 만들어야 합니다. 위에서 만든 Realm Tasks-to-Wit.ai를 이벤트 핸들러로 다시 만들어 보겠습니다.

서버 파일을 저장할 디렉터리를 만들고 package.json 파일을 생성합니다. 이 JSON 파일은 Node.js와 NPM에서 애플리케이션과 외부 디펜던시를 지정하기 위해 사용합니다.

npm init을 사용하면 대화식으로 이 파일을 만들 수 있습니다. 혹은 텍스트 에디터에서 간단한 내용을 직접 채워도 됩니다. Realm Tasks 예제를 만들기 위해 아래의 내용을 복사해서 package.json에 붙여넣습니다.

{
  "name": "RealmTasks",
  "version": "0.0.1",
  "description": "Use Realm Object Server's event-handling capabilities to add datetime reminders to your tasks.",
  "main": "index.js",
  "author": "Realm",
  "dependencies": {
    "realm": "^1.8.2",
    "node-wit": "4.2.0"
  }
}

package.json 파일이 제대로 설정됐다면 아래 명령어를 입력합니다.

npm install

이 명령어는 모든 모듈과 디펜던시에 대해 다운로드, 압축 해제, 설정을 수행합니다.

이벤트 핸들러에는 Wit.ai와 두 개의 Realm을 위한 접근 토큰이 필요합니다. 하나는 엔터프라이즈 에디션을 사용하기 위해 이메일로 받은 토큰입니다. 다른 하나는 애플리케이션이 어드민 유저로 로그인할 때 사용하는 Realm 오브젝트 서버의 “어드민 토큰”입니다. Realm 오브젝트 서버의 서버 인스턴스에서 아래 명령어로 해당 토큰을 볼 수 있습니다.

cat /etc/realm/admin_token.base64

이제 필요한 모든 것을 설정했으므로 이벤트 핸들링 코드를 넣을 파일을 생성할 차례입니다. index.js라는 이름으로 새 파일을 생성해서 아래 코드를 붙여넣습니다. 표시된 곳에 토큰을 추가하는 것을 잊지 마세요.

'use strict';

var fs = require('fs');
var Realm = require('realm');
const {Wit, log} = require('node-wit');

// 엔터프라이즈 에디션 접근 토큰
var token = "INSERT_YOUR_ACCESS_TOKEN";
Realm.Sync.setFeatureToken(token);

// Realm 어드민 토큰
var REALM_ADMIN_TOKEN = "INSERT_YOUR_ADMIN_TOKEN";

// wit.ai API의 서버 접근 토큰
var WIT_ACCESS_TOKEN = "INSERT_YOUR_WIT_TOKEN";

// Realm 오브젝트 서버 URL
var SERVER_URL = 'realm://YOUR_SERVER_URL:9080';
// var SERVER_URL = 'realm://127.0.0.1:9443'; // 로컬인 경우

// 글로벌 알림이 일치하는 모든 Realm의 변경 사항을 수신할 때 사용하는 경로
var NOTIFIER_PATH = ".*/realmtasks";

const client = new Wit({accessToken: WIT_ACCESS_TOKEN})

function isRealmObject(x) {
    return x !== null && x !== undefined && x.constructor === Realm.Object
}

var change_notification_callback = function(change_event) {
    let realm = change_event.realm;
    let changes = change_event.changes.Task;
    let taskIndexes = changes.modifications;

    console.log(changes);

    // 처리할 할 일 객체 가져오기
    var tasks = realm.objects("Task");

    for (var i = 0; i < taskIndexes.length; i++) {
        let taskIndex = taskIndexes[i];
        // 인덱스를 사용해서 Realm에서 할 일 객체 가져오기
        let task = tasks[taskIndex];
        if (isRealmObject(task)) {
            console.log("New task received: " + change_event.path);
            // wit.ai로부터 data 가져오기
            // node-wit: https://github.com/wit-ai/node-wit
            const client = new Wit({accessToken: WIT_ACCESS_TOKEN});
            client.message(task.text, {}).then((data) => {
                console.log("Response received from wit: " + JSON.stringify(data));
                var dateTime = data.entities.datetime[0];
                if (!dateTime) {
                    console.log("Couldn't find a date.");
                    return;
                }
                console.log("Isolated calculated date: " + dateTime.value);
                // 날짜를 쓰려면 클라이언트에 날짜 속성을 추가하고 마이그레이션해야 합니다.
                realm.write(function() {
                    task.date = new Date(dateTime.value);
                });
            })
            .catch(console.error);

            // 날짜를 쓰려면 클라이언트에 날짜 속성을 추가하고 마이그레이션해야 합니다.
            // realm.write(function() {
            //     task.date = ;
            // });

          }
    }
};

// 어드민 사용자 생성
var admin_user = Realm.Sync.User.adminUser(REALM_ADMIN_TOKEN);

// Realm 변경 사항에 따른 콜백
Realm.Sync.addListener(SERVER_URL, admin_user, NOTIFIER_PATH, 'change', change_notification_callback);

console.log('Listening for Realm changes across: ' + NOTIFIER_PATH);
// index.js의 끝

이 예제는 앞서 만든 Function처럼 동작하지만, Function 인터페이스 대신 코드로 세부 사항을 지정해야 합니다. 해당 이벤트 핸들러가 가상 경로가 /~/realmtasks인 특정 사용자의 개인 Realm 변경 사항을 수신하도록 지정했습니다. (~ 물결 문자는 와일드카드입니다) 이벤트 핸들러는 해당 Realm에서 업데이트된 Task 객체를 찾고, Task의 텍스트로 Wit.ai를 호출해서 datetime 객체가 반환된 경우 date 속성을 업데이트합니다. 마지막으로 이벤트 핸들링 스크립트의 아래 부분에서 어드민 유저가 모든 realmtasks Realm을 수신할 수 있도록 하는 코드가 있습니다.

위 코드를 실행하려면 index.js 파일이 있는 폴더에서 아래 명령어로 node를 시작합니다.

node index.js

마지막으로 노드 프로세스를 좀 더 안전하게 만들고 싶다면 PM2와 같은 프로세스 매니저를 사용해서 실행 후 크래시가 있는 경우 자동으로 이벤트 핸들러를 재시작할 수 있습니다.

엔터프라이즈 에디션으로 업그레이드

업그레이드 하려면 이메일로 데모 접근 토큰을 받아야 합니다. 먼저 Realm의 데이터를 그대로 유지한 채로 Realm 오브젝트 서버 프로페셔널 에디션을 중지하고 언인스톨해야 합니다. 이 작업은 서버상의 Unix 셸 프롬프트에서 수행할 수 있습니다.

Realm 오브젝트 서버를 실행하는 서버에 SSH 접속하고 다음 명령어를 입력하세요. (#로 시작하는 주석은 입력하지 않아도 됩니다)

# 언인스톨 전 서비스 중지
sudo systemctl stop realm-object-server

# 프로페셔널 에디션 언인스톨
sudo apt-get remove realm-object-server-professional

# 새로운 엔터프라이즈 에디션 토큰 설정
export PACKAGECLOUD_TOKEN=<the token you received via email>

# 새로운 패키지 저장소 설정
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update

# Realm 오브젝트 서버 설치
sudo apt-get install realm-object-server-enterprise

# 서비스 활성화 및 시작
sudo systemctl enable realm-object-server
sudo systemctl start realm-object-server

같은 IP 주소, 포트, 어드민 계정으로 Realm 오브젝트 서버 대시보드에 접근해서 이전 섹션에서 생성한 Realm을 볼 수 있어야 합니다..

동기화 워커 추가

동기화 워커는 Realm을 저장하고 Realm 모바일 클라이언트에 응답하며 변경 내용을 Realm의 충돌 해소 알고리즘과 병합하는 프로세스입니다. Realm 오브젝트 서버의 엔터프라이즈 에디션은 서버에 기본 설정 파일로 설치된 동기화 워커가 제공됩니다. 인증을 처리하고 Realm 모바일 클라이언트 요청을 라우팅하는 동기화 워커를 보안, 내결함성 및 로드 분리 문제 등의 이유로 Realm 오브젝트 서버에서 분리하고자 할 수도 있습니다. 기존 아키텍처에 추가 동기화 워커를 추가하는 방법을 안내하겠습니다.

가장 먼저 할 일은 새 동기화 워커를 위한 새로운 EC2 인스턴스를 시작하는 것입니다. 이 페이지의 시작 부분에 있는 안내대로 EC2 인스턴스를 시작하고 새 인스턴스에 SSH로 접속한 다음 Packagecloud에서 realm-sync-worker 패키지를 설치합니다.

# 프로페셔널 에디션 토큰 설정
export PACKAGECLOUD_TOKEN=<the token you received via email>

# 새로운 패키지 저장소 설정
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update

# 동기화 워커 설치
sudo apt-get install realm-sync-worker

# 서비스 활성화 및 시작
sudo systemctl enable realm-sync-worker
sudo systemctl start realm-sync-worker

위 명령어로 동기화 워커가 별도의 호스트에 설치됩니다. 새로운 동기화 워커에 연결하려면 Realm 오브젝트 서버의 설정 파일을 편집해야 합니다. Realm 오브젝트 서버 인스턴스로 SSH 접속하고 원하는 텍스트 에디터로 /etc/realm/configuration.yml을 엽니다.

Sync 섹션을 찾아서 다음 내용의 주석 처리를 제거하고 새로 만든 서버의 인터널 DNS나 IP 주소를 추가합니다.

# /etc/realm/configuration.yml

sync:
  servers:
    - id: '0'
    address: '<internal DNS or IP of sync worker server>'
    port: 7800

동기화 워커의 ID가 같다면 sync-worker의 기본 IP 주소는 바뀔 수 있으므로, 기존 동기화 워커가 실패했기 때문에 새로운 동기화 워커를 온라인 상태로 만드는 장애 조치 시나리오에서는 ID 값을 유지한 채로 새 sync-worker를 시작할 수 있습니다. 사실 DNS 이름을 사용한다면, 오류 발생 시 동기화 워커를 가리키도록 네임 서버를 간단히 업데이트할 수 있습니다. 자세한 내용은 장애 조치 문서를 참조하세요.

설정 파일을 편집했다면 오브젝트 서버를 재시작합니다. 셸 프롬프트에서 다음 명령어를 사용하세요.

systemctl restart realm-object-server

이 경우 시스템 로그에서 다음과 같은 에러가 보입니다.

[syncProxy] internal error: Error: connect ECONNREFUSED

이 에러는 동기화 워커가 아직 연결을 수신 대기하도록 설정하지 않았기 때문에 발생합니다. 설정 방법은 잠시 후에 다룰 예정이며, Realm 오브젝트 서버의 머신에서 공개 키를 먼저 복사해야 합니다. 이 파일은 /etc/realm/token-signature.pub에 있습니다. 이 파일은 동기화 워커와 Realm 오브젝트 서버 모두에서 같아야 합니다. scp를 사용하면 파일을 복사할 수 있습니다.

scp /etc/realm/token-signature.pub sync-worker-ip:/etc/realm/token-signature.pub

(sync-worker-ip를 동기화 워커 머신의 실제 IP 주소나 DNS 이름으로 교체합니다) FTP로 키를 전송하거나 Realm 오브젝트 서버 시스템의 공개 키를 클립 보드에 복사해도 됩니다. 그다음 이를 동기화 워커의 /etc/realm-token-signature.pub 에 붙여넣습니다.

이제 다시 동기화 워커의 SSH로 돌아가서 동기화 워커의 설정 파일, /etc/realm/sync-worker-configuration.yml을 편집합니다. network 블록의 sync 섹션의 주석 처리를 제거하세요.

# /etc/realm/sync-worker-configuration.yml

network:
    sync:
        listen_address: '0.0.0.0'

0.0.0.0모든 네트워크 인터페이스를 수신하므로 주의하세요. 실제 사용할 때는 Realm 오브젝트 서버의 실제 IP를 지정하는 것이 안전합니다.

마지막으로 동기화 워커를 재시작하세요.

systemctl restart realm-sync-worker

이 프로세스를 다시 반복해서 여러 개의 동기화 워커를 추가하고 수평 스케일링으로 부하를 분산할 수 있습니다.

백업 클라이언트 추가

시스템 장애 또는 중단 시에 Realm 데이터를 정기적으로 백업해서 내결함성을 높일 수 있습니다. Realm은 동기화 워커와 동기화된 각 변경 집합을 백업 클라이언트로 보내는 연속 백업 기능을 제공합니다.

백업 클라이언트를 설정하는 과정은 동기화 워커를 설정하는 것과 비슷합니다.

# 엔터프라이즈 에디션 토큰 설정
export PACKAGECLOUD_TOKEN=<the token you received via email>

# 새로운 패키지 저장소 설정
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update

# 백업 클라이언트 설치
sudo apt-get install realm-object-server-backup-client

시작하기 전 백업 클라이언트의 설정 파일을 편집하세요.

# /etc/realm/object-server-backup-client.yml

sync:
  servers:
    - id: '0'
    address: '<internal DNS or IP of sync worker server>'
    port: 7800

이제 클라이언트를 시작합니다.

# 서비스 활성화 및 시작
sudo systemctl enable realm-object-server-backup-client
sudo systemctl start realm-object-server-backup-client

마지막으로 동기화 워커로 SSH 접속한 후에 listen_address 줄을 주석 처리하고 수정합니다.

# /etc/realm/sync-worker-configuration.yml

network:
    sync:
        listen_address: '0.0.0.0'

동기화 워커를 재시작합니다.

systemctl restart realm-sync-worker

이제 백업 클라이언트가 동기화 워커에 설정된 모든 Realm의 전체 데이터 세트를 가지게 됩니다.

백업 클라이언트로 장애 조치를 준비하고 해당 동기화 워커를 시작해서 동기화 워커를 대신할 수 있도록 하는 것이 가장 좋습니다. 이렇게 하려면 시작하기 전에 백업 클라이언트에 동기화 워커를 미리 설치해야 합니다. 백업 클라이언트로 SSH 접속합니다.

sudo apt-get install realm-sync-worker

백업 클라이언트가 저장한 곳과 같은 폴더에서 Realm을 읽기 시작하도록 동기화 워커의 설정을 수정합니다.

# /etc/realm/sync-worker-configuration.yml

# root_path가 백업 클라이언트 폴더 저장소를 가리키도록 변경
storage:
  root_path: '/var/lib/realm-object-server-backup-client'

# listen_address 줄의 주석을 해제하고 올바르게 설정
network:
  sync:
    listen_address: '0.0.0.0'

이제 원래 동기화 워커가 실패할 경우 백업 클라이언트 프로세스를 중지하고 준비한 동기화 워커를 시작해서 장애 조치를 할 수 있습니다.

sudo systemctl stop realm-object-server-backup-client
sudo systemctl start realm-sync-worker

자세한 내용은 프로페셔널/엔터프라이즈 에디션 문서의 장애 조치 섹션을 참고하세요.

장애 조치 이후에는 이 서버를 가리키는 새로운 백업 클라이언트를 시작해야 합니다. 이는 CloudFormation 이벤트 알림을 보내는 Auto Scaling Group 리소스와 같은 도구를 통해 자동으로 실행될 수도 있습니다.

모니터링 시스템 추가

이제 다음 작업을 시작해 보겠습니다. 먼저 모니터링 시스템을 설정하고 이를 Realm의 분산 시스템에 통합합니다. Realm은 메트릭에 statsd 엔드포인트를 사용합니다. 여기서는 Grafana와 InfluxDB로 서버를 설정하는 방법을 설명하지만, statsd를 호환한다면 다른 시스템도 사용할 수 있습니다.

다른 인프라와는 별도의 전용 서비스를 사용하는 것이 가장 좋습니다. 새 서버를 설정하고 SSH 접속해서 다음 단계대로 수행하세요. (# 표시된 줄은 주석입니다)

# 패키지 소스 설정
curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

# 저장소 업데이트
sudo apt-get update

# InfluxDB 설치
sudo apt-get install influxdb

# Influx 시작
sudo systemctl start influxdb

# influx 명령어로 데이터베이스 생성
influx
> CREATE DATABASE realmdemo

# 아래와 비슷한 결과가 되도록 SHOW DATABASES 명령어 사용
> SHOW DATABASES
name: databases
name
----
_internal
realmdemo

> exit

동기화 워커에 Telegraf를 설치하고 모니터링 서버로 내보내기 위한 통계를 수집해야 하므로 동기화 워커 서버에 SSH 접속합니다.

# Influxdb 저장소로 패키지 소스 설정
curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

# 저장소 업데이트
sudo apt-get update

# Telegraf 설치
sudo apt-get install telegraf

다음으로 텍스트 에디터에서 /etc/telegraf/telegraf.conf를 열고 InfluxDB 서버와 데이터베이스 이름을 outputs.influxdb 섹션에 추가합니다.

[[outputs.influxdb]]
  urls = ["http://<IP_OF_MONITORING_SERVER>:8086"] # required
  database = "realmdemo" # required

또한, Realm 오브젝트 서버의 statsd에 대한 입력을 위 파일의 inputs.statsd 섹션에 추가합니다.

[[inputs.statsd]]
  service_address = ":8125"

이제 Telegraf를 활성화하고 시작합니다.

sudo systemctl enable telegraf
sudo systemctl start telegraf

InfluxDB와 Telegraf를 실행했으니 이제 Grafana를 설정해서 Realm 데이터와 메트릭을 볼 차례입니다.

# Grafana 저장소 설정
echo "deb https://packagecloud.io/grafana/stable/debian/ wheezy main" | sudo tee /etc/apt/sources.list.d/grafana.list
curl https://packagecloud.io/gpg.key | sudo apt-key add -

# 저장소 업데이트
sudo apt-get update

# Grafana 설치
sudo apt-get install grafana

# Grafana 활성화 및 실행
sudo systemctl enable grafana-server
sudo systemctl start grafana-server

이제 모니터링 서버의 공용 IP 포트인 3000 포트로 이동해서 웹 브라우저에서 Grafana 서버를 찾아보겠습니다. 기본 로그인은 admin/admin을 사용합니다.

다음으로 Add a Data Source를 추가하고 InfluxDB 정보를 넣습니다.

이제 New Dashboard를 선택해서 그래프에 메트릭을 추가할 수 있습니다. Realm에 특화된 메트릭은 realm_으로 시작합니다. 분산 시스템 내의 각 Realm 호스트에 Telegraf를 추가하세요.

Realm 플랫폼 PE/EE 문서에서 지원되는 전체 메트릭 리스트를 볼 수 있습니다.

로그 시스템 추가

다음으로 다양한 Realm 설정 구성 요소에서 정기적으로 로그를 가져오는 전용 로그 서버를 설정해 보겠습니다. Elasticsearch와 Kibana를 Filebeat 유틸리티로 Realm에 통합하는 방법을 알려 드리겠습니다.

Elasticsearch와 Filebeat 설치

먼저 로그 서버로 사용할 새로운 서버나 EC2 인스턴스를 설정한 다음 SSH로 접속합니다.

# 기존에 설치된 Java가 있는 경우 삭제
sudo apt-get remove --purge openjdk*

# Oracle 저장소 추가
sudo add-apt-repository -y ppa:webupd8team/java

# 저장소 업데이트 및 Java와 wget 설치
sudo apt-get update
sudo apt-get -y install oracle-java8-installer
sudo apt-get install wget

그다음 Elasticsearch를 설치합니다.

# Elasticsearch의 키와 저장소 추가
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
sudo apt-get update

# Elasticsearch 설치
sudo apt-get install elasticsearch
# Elasticsearch 활성화 및 시작
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch.service
sudo systemctl start elasticsearch.service

Elasticsearch가 설치되고 기본 값인 9200 포트를 수신 대기합니다. 다음으로 Realm 오브젝트 서버와 Realm 동기화 워커에 Filebeat를 설치합니다. 그 후 로그 머신이 아닌 동기화 워커 머신에 SSH 접속합니다.

# Filebeat 저장소 설정 및 설치
echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
sudo apt-get update
sudo apt-get install filebeat

/etc/filebeat/filebeat.yml에 있는 Filebeat 설정 파일을 편집합니다. YAML 파일이므로 인덴트에 주의하세요!

input:
      paths:
        - /var/log/realm-sync-worker.log
      tail_files: true
output:
  elasticsearch:
    hosts: ["<logging_server_hostname>:9200"]

이제 설정 변경 사항을 반영하도록 Filebeat을 시작합니다.

sudo systemctl start filebeat

Realm 오브젝트 서버에서 로그를 받아와야 하므로 해당 호스트에 SSH 접속한 후 여기에도 Filebeat를 설정합니다.

# 저장소 설정 및 설치
echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
sudo apt-get update
sudo apt-get install filebeat

앞서와 비슷하게 /etc/filebeat/filebeat.yml에 있는 Filebeat 설정 파일을 수정하지만, 로그 파일 경로는 달라집니다.

input:
      paths:
        - /var/log/realm-object-server.log
      tail_files: true
output:
  elasticsearch:
    hosts: ["<logging_server_hostname>:9200"]

여기에서도 Filebeat을 시작합니다.

sudo systemctl start filebeat

그다음 로그 서버로 다시 SSH 접속합니다. /etc/elasticsearch/elasticsearch.yml에 있는 설정 파일의 network.host 부분을 편집해서 들어오는 Filebeat 연결을 위한 Elasticsearch를 설정합니다.

network.host: '0.0.0.0'

마지막으로 Elasticsearch를 재시작합니다.

sudo systemctl restart elasticsearch

Kibana 설치

Kibana는 로그 시각화와 UI를 제공하는 Elasticsearch의 다른 제품입니다.

로그 서버로 SSH 접속합니다. (여기저기 SSH를 접속하는 작업은 이제 거의 끝났습니다)

이미 Elasticsearch 패키지 저장소가 설정돼 있으므로 쉽게 설치할 수 있습니다.

sudo apt-get update
sudo apt-get install kibana

# 서비스 활성화 및 시작
sudo systemctl enable kibana
sudo systemctl start kibana

웹 브라우저에서 http://<logging_server_hostname>:5601로 이동해서 Kibana 서버에 로그인합니다. Kibana 설정 페이지가 열리면 filebeat-*이라는 이름으로 인덱스 패턴을 생성하고 Time-field 이름으로 @timestamp를 넣습니다.

Discover 탭에서 15분 등의 recent time interval을 선택합니다. Realm 오브젝트 서버에 대한 연결 생성을 시작하면 다음처럼 Kibana가 로그를 생성하는 것을 볼 수 있습니다.

마무리

Realm 플랫폼을 시작하고 배포하는 방법과 동기화 및 로그인을 앱에 활성화하고 서버사이드 기능 및 이벤트 핸들링을 추가하며, 동기화 워커 및 백업 클라이언트를 추가하는 방법을 보여드렸습니다. 앞으로도 프로젝트에 필요한 경우 이 문서의 관련 내용을 참고할 수 있습니다.

여러분의 순조로운 개발을 도울 수 있었기를 바랍니다. 언제든지 도움이 필요하면 Realm 담당자에게 연락하세요!