Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1060 lines (761 sloc) 39.2 KB

Table of Contents

  1. 개요

  2. JAVA API 서비스 미터링 개발가이드

#

1. 개요

##

1.1. 문서 개요

###

1.1.1. 목적

본 문서(Java API 서비스 미터링 적용개발 가이드)는 파스-타 플랫폼 프로젝트의 미터링 플러그인과 Java API 미터링 서비스 애플리케이션을 연동시켜 API 서비스를 미터링 하는 방법에 대해 기술 하였다.

###

1.1.2. 범위 본 문서의 범위는 파스-타 플랫폼 프로젝트의 JAVA API 서비스 애플리케이션에 대한 미터링 방법에 대한 개발과 CF-Abacus 연동에 대한 내용으로 한정되어 있다.

본 문서는 API 미터링 서비스 애플리케이션을 Java 언어로 작성 하는 것에 대해 기술 한다.

본 문서는 API 서비스 고유의 비즈니스 로직은 구현 하지 않으며, API 서비스 호출 시의 미터링을 하는 기능만 구현 한다.

본 문서에서 언급 하는 “API 서비스를 사용하는 애플리케이션”은 별도로 제공 하는 Node.js API 미터링 개발 가이드를 참고 하여 개발 한다.

###

1.1.3. 참고 자료

##

2. JAVA API 서비스 미터링 개발가이드 ###
2.1 개요

API 서비스 애플리케이션을 Java 언어로 작성 한다. API 서비스는 서비스 요청을 처리함과 동시에 API 사용 내역을 CF-ABACUS에 전송하는 애플리케이션을 작성 한다.

Java_Api_Service_Metering_Image01

기능 설명
Runtime 미터링/등급/과금 정책 API 서비스 제공자가 제공하는 서비스에 대한 각종 정책 정의 정보. JSON 형식으로 되었으며, 해당 정책을 CF-ABACUS에 등록하면 정책에 정의한 내용에 따라 API 사용량을 집계 한다.
정책은 서비스 제공자가 정의해야 하며, JSON 스키마는 다음을 참조한다.
https://github.com/cloudfoundry-incubator/cf-abacus/blob/master/doc/api.md
서비스 브로커 API Cloud Controller와 Service Broker 사이의 규약으로써 서비스 브로커 API 개발에 대해서는 다음을 참조한다.
https://github.com/OpenPaaSRnD/Documents/blob/master/Development-Guide/ServicePack_develope_guide.md#11
서비스 API 서비스 제공자가 제공하는 API 서비스 기능 및 API 사용량을 CF-ABACUS에 전송하는 기능으로 구성되었다.
대시보드 서비스를 제공하기 위한 인증, 서비스 모니터링 등을 위한 대시보드 기능으로 서비스 제공자가 개발해야 한다.
CF-ABACUS CF-ABACUS 핵심 기능으로써 수집한 사용량 정보를 집계한다.
CF-ABACUS은 CF 설치 후, CF에 마이크로 서비스 형태로 설치한다. 자세한 사항은 다음을 참조한다.
https://github.com/cloudfoundry-incubator/cf-abacus

※ 본 개발 가이드는 API 서비스 개발에 대해서만 기술하며, 다른 컴포넌트의 개발 또는 설치에 대해서 링크한 사이트를 참조한다.

##

2.2 개발환경 구성

Java 애플리케이션 개발을 위해 다음과 같은 환경으로 개발환경을 구성 한다.

  • CF release: v226 이상
  • java version "1.8.0_101"
  • springBootVersion : 1.3.0.BUILD-SNAPSHOT
  • gradle 2.14
  • Spring Tool Suite 혹은 Eclipse

###

2.2.1 CF-Abacus 설치

별도 제공하는 Abacus 설치 가이드를 참고하여 CF-Abacus를 설치한다.

##

2.3 샘플 API 서비스 개발

샘플 api 서비스는 서비스 요청이 있는 경우, 해당 요청에 대한 응답 처리와 api 서비스 요청에 대한 미터링 정보를 CF-ABACUS에 전송하는 처리를 한다.

###

2.3.1 gradle 프로젝트를 생성

프로젝트 디렉터리를 생성하고, gradle 프로젝트로 초기화 한다

$ mkdir sample_api_java_service // 프로젝트 디렉토리
$ cd sample_api_java_service/
~/sample_api_java_service $ gradle init --type java-library // gradle 초기화
: wrapper
: init

BUILD SUCCESSFU

Total time: 2.435 secs

This build could be faster, please consider using the Gradle Daemon: 
https://docs.gradle.org/2.14/userguide/gradle_daemon.html

###

2.3.2 샘플 API 서비스 형상

Java_Api_Service_Metering_Image02

의존성 및 프로퍼티 형상 설명

파일 목적
build.gradle 애플리케이션에 필요한 의존성 정보를 기술
.gitignore Git을 통한 형상 관리 시, 형상 관리를 할 필요가 없는 파일 또는 디렉토리를 설정한다.
manifest.yml 애플리케이션을 파스-타 플랫폼에 배포 시 적용하는 애플리케이션에 대한 환경 설정 정보
애플리케이션의 이름, 배포 경로, 인스턴스 수 등을 정의할 수 있다.
gradlew Linux 환경에서 사용하는 gradlew 빌드 실행 파일
gradle 초기화 시 자동 생성 된다.
gradlew.bat Window 환경에서 사용하는 gradle 빌드 실행 파일
gradle 초기화 시 자동 생성 된다.
settings.gradle gradlew 실행 시 적용하는 환경 설정 파일
gradle 초기화 시 자동 생성 된다.

Java 파일 형상 설명

파일 목적
MeteringConfig 애플리케이션 구동 시 metering.properties를 로드 한다
MeteringAuthService 파스-타 플랫폼 상의 UAA 서버에서 abacus-usage-collector 에 대한 접근 권한 토큰을 취득하여 리턴 한다.
MeteringService API 서비스 사용 요청 이 SampleApiJavaServiceController 에서 처리 될 때 API 서비스 처리에 대해 미터링이 적용된 사용량 보고서를 abacus-usage-collector 에 리포팅 한다.
SampleApiJavaServiceApplication SpringBoot이 구동할 때, SpringBoot애플리케이션에 필요한 context 객체 들을 로드 한다.
SampleApiJavaServiceController API 서비스 사용 요청을 처리하는 REST Controller.
본 샘플 애플리케이션에서는 API 서비스 고유의 비즈니스 로직은 구현 하지 않았으며, API 사용량을 abacus-collector에 전송하는 기능만 수행 한다.
application.properties SpringBoot이 구동할 때, spring 에 필요한 property
metering.properties API 사용량을 abacus-collector 전송 시에 설정 할 property 들이 정의 되어 있다.

###

2.3.3 의존성 및 프로퍼티의 설정

  • build.gradle

    샘플 Api 서비스 애플리케이션이 사용하는 의존성에 대해 기술한다.

     중략..
    
     dependencies {
    
     	// https://mvnrepository.com/artifact/org.springframework/spring-test
     	compile group: 'org.springframework', name: 'spring-test', version: '2.5'
          
         providedRuntime("org.springframework.boot:spring-boot-starter-tomcat:${springBootVersion}")
         compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
     
         // 미터링 사용량 객체 생성 dependency
         compile("org.json:json:20160212")    // Json object 생성시
         compile("com.sun.jersey:jersey-bundle:1.18.1") ")    // https connection 생성시
         compile("com.googlecode.json-simple:json-simple:1.1") // json parse 
         compile("commons-codec:commons-codec:1.5") // https connection 생성시
     }	
    
     후략..
    
  • manifest.yml

    앱을 CF에 배포할 때 필요한 설정 정보 및 앱 실행 환경에 필요한 설정 정보를 기술 한다.

applications:
- name: sample-api-node-service  # 애플리케이션 이름
  memory: 512M # 애플리케이션 메모리 사이즈
  instances: 1 # 애플리케이션 인스턴스 개수
  host: sample-api-java-service
  path: ./build/libs/sample_api_java_service.jar # 배포될 애플리케이션의 위치
  env:
    SPRING_PROFILES_ACTIVE : cloud
  • metering.properties

    API 서비스의 사용량 정보를 abacus-collector에 전송 할 때, 필요한 설정 정보 및 계정 정보를 기술 한다.

    # abacus usage collector RESTAPI 의 주소
    
    abacus.collector = https://abacus-usage-collector.<CF도메인/v1/metering/collected/usage
    
    # abacus usage collector 가 secured 모드 true / 아닐 경우 false
    
    abacus.secured = true
    
    # 파스-타 플랫폼의 uaa server
    
    uaa.server = https://uaa.<CF도메인>
    
    # abacus usage collector RESTAPI 계정 정보 및 사용권한 (UAA server에 미리 설정)
    
    uaa.client.id = <abacus.usage.read/write scope 권한을 가진 ID>
    
    uaa.client.secret = <abacus.usage.read/write scope 권한을 가진 ID 비밀번호>
    
    uaa.client.scope = abacus.usage.object-storage.write,abacus.usage.object-storage.read
    

###

2.3.4 MeteringAuthService 클래스

UAA 서버 URL 및 계정 정보를 참조 하여, UAA token 을 취득하여 리턴 한다.

public String getUaacTokenHTTPS () throws MalformedURLException {

	String authToken = "";
	String urlStr = authServer + "/oauth/token?grant_type=client_credentials&scope=" + encodeURIComponent(scope);
	StringBuffer sb = new StringBuffer();

	try {

		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}

			public void checkClientTrusted(X509Certificate[] certs, String authType) {
			}

			public void checkServerTrusted(X509Certificate[] certs, String authType) {
			}
		} };

		SSLContext sc = SSLContext.getInstance("SSL");
		sc.init(null, trustAllCerts, new java.security.SecureRandom());
		HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
		URL url = new URL(urlStr);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        String authHeader = getAuthKey(clientId, clientSecret);
        conn.setRequestProperty("authorization", authHeader);

		InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
		BufferedReader br = new BufferedReader(in);

		String line;
		while ((line = br.readLine()) != null) {
			sb.append(line).append("\n");
		}
		
		authToken= parseAuthToken(sb.toString());
		
		br.close();
		in.close();
		conn.disconnect();

	} catch (Exception e) {
		System.out.println(e.toString());
	}

	return authToken;
}

metering.properties 에서 취득한 계정 정보를 BASE64 로 인코딩 한다.

public String getAuthKey(String id, String secret) throws Exception {	
	String authKey = ""; 	
	try {	
		String encodedConsumerKey = URLEncoder.encode(id, "UTF-8");
		String encodedConsumerSecret = URLEncoder.encode(secret, "UTF-8");
		String fullKey = encodedConsumerKey + ":" + encodedConsumerSecret;
		byte[] encodedBytes = Base64.encodeBase64(fullKey.getBytes());		
		authKey = "Basic " + new String(encodedBytes);	
	} catch (Exception e) {
		e.printStackTrace();			
		throw e;
	}	
	return authKey;
}

UAA SEVER 에서 리턴 받은 JSON 오브젝트 에서 access_token 을 추출한다.

private String parseAuthToken(String jsonStr) throws ParseException{		
	String barerStr;		
	JSONParser jsonParser = new JSONParser();
	JSONObject jsonObject = (JSONObject) jsonParser.parse(jsonStr);
	barerStr = (String) jsonObject.get("access_token");		
	return barerStr;
}

###

2.3.5 MeteringService 클래스 abacus-collector의 Auth 설정 정보에 따라, 전송 방식에 대한 분기 처리를 한다.

public void reportUsageData(String orgId, String spaceId, String appId, String planId) throws Exception {
	JSONObject serviceUsage = buildServiceUsage(orgId, spaceId, appId, planId);

	if (SECURED.equals(abacusSecured)) {
		reportUsageDataHTTPS(serviceUsage);
	} else {
		reportUsageDataHTTP(serviceUsage);
	}
}

API 사용량을 Abacus-collector에 전송하기 위해, CF 또는 인증 서버로부터 토큰을 취득하여 HTTP header에 설정하고 HTTPS Connection을 생성 한다.

public void reportUsageDataHTTPS(JSONObject serviceUsage) throws Exception {
	StringBuffer sb = new StringBuffer();
	try {
		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}

			public void checkClientTrusted(X509Certificate[] certs, String authType) {
			}  // 인증서를 생성 한다.

			public void checkServerTrusted(X509Certificate[] certs, String authType) {
			}
		} };

		SSLContext sc = SSLContext.getInstance("SSL");
		sc.init(null, trustAllCerts, new java.security.SecureRandom());
		HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

		URL url = new URL(collectorUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("POST");
		conn.setDoInput(true);
		conn.setDoOutput(true);
		conn.setUseCaches(false);

		conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");

		String bareStr = "bearer " + meteringAuthService.getUaacTokenHTTPS();
		conn.setRequestProperty("Authorization", bareStr);

		byte[] out = serviceUsage.toString().getBytes(StandardCharsets.UTF_8);

		DataOutputStream dout = new DataOutputStream(conn.getOutputStream());
		dout.write(out);
		dout.close();

		InputStreamReader in = new InputStreamReader((InputStream) conn.getInputStream());
		BufferedReader br = new BufferedReader(in);

		String line;
		while ((line = br.readLine()) != null) {
			sb.append(line).append("\n");
		}

		System.out.println(sb.toString());
		System.out.println(serviceUsage + " was repoerted.");

		br.close();
		in.close();
		conn.disconnect();

	} catch (Exception e) {
		Exception se = new Exception(e);
		throw se;
	}
}

API 사용량을 Abacus-collector에 전송하기 위한 HTTP header를 설정하고 HTTP Connection을 생성 한다.

public void reportUsageDataHTTP(JSONObject serviceUsage) throws Exception {

	try {
		URL url = new URL(collectorUrl);
		URLConnection con = url.openConnection();
		HttpURLConnection http = (HttpURLConnection) con;
		http.setRequestMethod("POST"); // PUT is another valid option
		http.setDoOutput(true);
		http.setDoInput(true);
		http.setUseCaches(false);

		byte[] out = serviceUsage.toString().getBytes(StandardCharsets.UTF_8);
		int length = out.length;

		http.setFixedLengthStreamingMode(length);
		http.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
		http.connect();

		try (OutputStream os = http.getOutputStream()) {
			os.write(out);
		}

	} catch (IOException e) {
		e.printStackTrace();
		throw new Exception(e);
	}
}

Abacus-collector에 전송 할 API 서비스 사용량 JSON을 생성 한다.

private JSONObject buildServiceUsage (String orgId, String spaceId, String appId, String planId)
		throws JSONException {

	LocalDateTime now = LocalDateTime.now();
	Timestamp timestamp = Timestamp.valueOf(now);

	JSONObject jsonObjectUsage = new JSONObject ();

	jsonObjectUsage.put ("start", timestamp.getTime());
	jsonObjectUsage.put ("end", timestamp.getTime());
	jsonObjectUsage.put ("organization_id", orgId);
	jsonObjectUsage.put ("space_id", spaceId);
	jsonObjectUsage.put ("consumer_id", "app:" + appId);
	jsonObjectUsage.put ("resource_id", RESOURCE_ID);
	jsonObjectUsage.put ("plan_id", planId);
	jsonObjectUsage.put ("resource_instance_id", appId);

	JSONArray measuredUsageArr = new JSONArray ();
	JSONObject measuredUsage1 = new JSONObject ();
	JSONObject measuredUsage2 = new JSONObject ();
	JSONObject measuredUsage3 = new JSONObject ();

	int quantity = 0;

	if (STANDARD_PLAN_ID.equals(planId)) {
		quantity = PLAN_STANDARD_QUANTITY;
	} else if (EXTRA_PLAN_ID.equals(planId)) {
		quantity = PLAN_EXTRA_QUANTITY;
	}

	measuredUsage1.put ("measure", MEASURE_1);
	measuredUsage1.put ("quantity", quantity);
	measuredUsageArr.put(measuredUsage1);
	measuredUsage2.put ("measure", MEASURE_2);
	measuredUsage2.put ("quantity", 1);
	measuredUsageArr.put(measuredUsage2);
	measuredUsage3.put ("measure", MEASURE_3);
	measuredUsage3.put ("quantity", 0);
	measuredUsageArr.put(measuredUsage3);

	jsonObjectUsage.put ("measured_usage", measuredUsageArr);
	return jsonObjectUsage;
}	
  • API 서비스 미터링 전송 항목 (전송 리포트 JSON 상세)
항목명 유형 설명 예시
start UNIX Timestamp API처리 시작 시각
end UNIX Timestamp API처리 응답 시각
space_id String API를 호출한 앱의 영역 ID d98b5916-3c77-44b9-ac12-04456df23eae
resource_id String API 자원 ID sample_api
plan_id String API 미터링 Plan ID basic
resource_instance_id String API를 호출한 앱 ID d98b5916-3c77-44b9-ac12-04d61c7a4eae
measured_usage Array 미터링 항목 -
measure String 미터링 대상 명 api_calls
quantity Number 해당 API 요청에 대한 API 처리 횟수 10

※ JSON 변환 예제

{
  "start": 1396421450000,
  "end": 1396421451000,
  "organization_id": "us-south:54257f98-83f0-4eca-ae04-9ea35277a538",
  "space_id": "d98b5916-3c77-44b9-ac12-04456df23eae",
  "consumer_id": "app:d98b5916-3c77-44b9-ac12-045678edabae",
  "resource_id": "sample_api",
  "plan_id": "basic",
  "resource_instance_id": "d98b5916-3c77-44b9-ac12-04d61c7a4eae",
  "measured_usage": [
    {
      "measure": "api_calls",
      "quantity": 10
    }
  ]

###

2.3.6 SampleApiJavaServiceController 클래스

서비스 사용 요청을 처리하는 REST Controller. 본 샘플 애플리케이션에서는 미터링을 하는 기능만 수행 한다.

중략..
@RequestMapping (value = "/plan1", method = RequestMethod.POST)
public ResponseEntity<String> serviceAPIPlan01(@RequestBody String input) throws Exception {	
	JSONParser jsonParser = new JSONParser ();
	JSONObject jsonObject = (JSONObject) jsonParser.parse(input);	
	String orgId = (String) jsonObject.get("organization_id");
	String spaceId = (String) jsonObject.get("space_id");	
	String appId = (String) jsonObject.get("consumer_id");	
	String planId = (String) jsonObject.get("plan_id");	
	JSONObject serviceKeyOBJ = (JSONObject) jsonObject.get("credential");	
	String serviceKey = (String) serviceKeyOBJ.get("serviceKey");	
	
	if(!SERVICE_KEY.equals(serviceKey))
		return new ResponseEntity<>("credential is wrong", HttpStatus.UNAUTHORIZED);
	
	meteringService.reportUsageData(orgId, spaceId, appId, planId);		
	
	String successStr = "orgId:" + orgId + "/ spaceId:" + spaceId + "/ appId:" + appId + "/ planId:" + planId + " was reported to abacus collector.";
	
	return new ResponseEntity<>(successStr, HttpStatus.OK);
} 
후략..

##

2.4 API 서비스 연동 샘플 애플리케이션

본 가이드에서는 API 서비스를 호출하는 애플리케이션의 개발에 대해서는 기술하지 않는다. 샘플 애플리케이션의 개발에 대해서는 Node.js API미터링 개발 가이드의Api****서비스 연동 애플리케이션 개발을 참고 한다.

###

2.4.1 API 서비스 연동 샘플 애플리케이션 인터페이스 항목

####1. API 서비스 엔드 포인트

GET|POST|PUT|DELETE <api_service_restful_api>

####2. API 서비스 미터링 전송 항목

항목명 유형 설명 예시
org_id String API 서비스를 요청한 앱의 조직 ID 54257f98-83f0-4eca-ae04-9ea35277a538
space_id String API 서비스를 요청한 앱의 영역 ID d98b5916-3c77-44b9-ac12-04456df23eae
consumer_id String API 서비스를 요청한 앱 ID d98b5916-3c77-44b9-ac12-045678edabae
instance_id String API 서비스를 요청한 앱의 자원 인스턴스 ID d98b5916-3c77-44b9-ac12-045678edabad
plan_id String 앱의 요청한 API 서비스의 plan ID basic
credentials JSON 서비스 요청에 필요한 credential 항목을 설정한다. credentials: {
key: value,

}
inputs JSON 서비스 요청에 필요한 입력 정보를 설정한다. inputs: {
key:value,
...
}

####3. API 서비스 미터링 전송 항목 예제

{
  organization_id: 'd6ce3670-ab9c-4453-b993-f2821f54846b',
  space_id: 'ab63eaed-7932-4f24-804d-dccb40a68752',
  consumer_id: 'ff7476f9-f5b6-420c-96f0-ac39be43de8c',
  instance_id: 'ff7476f9-f5b6-420c-96f0-ac39be43de8c',
  plan_id: 'standard',
  credential: {
	'serviceKey': '[cloudfoundry]',
	'url': 'http://localhost:9602/plan1'
  },
  inputs: {
	key1: 'val1',
	key2: 'val2'
  }
}

##

2.5. 미터링/등급/과금 정책

서비스, 그리고 서비스 제공자 마다 미터링/등급/과금 정책 다르기 때문에 본 가이드에서는 정책의 개발 예제를 다루지는 않는다. 다만 CF-ABACUS에 적용할 수 있는 형식에 대해 설명한다.

###

2.5.1. 미터링 정책

미터링 정책이란 수집한 미터링 정보에서 미터링 대상의 지정 및 집계 방식을 정의한 JSON 형식의 오브젝트이다. 서비스 제공자는 미터링 정책 스키마에 맞춰 서비스에 대한 정책을 개발한다.

####1. 미터링 정책 스키마

항목명 유형 필수 예시
plan_id String O API 미터링 Plan ID
measures Array 최소 하나 API 미터링 정보 수집 대상 정의
name String O 미터링 정보 수집 대상 명
unit String O 미터링 정보 수집 대상 단위
metrics Array 최소 하나 API 미터링 집계 방식 정의
name String O 미터링 정보 수집 대상 명
unit String O 미터링 정보 수집 대상 단위
meter String X 미터링 정보에 대해서 수집 단계에 적용하는 계산식 또는 변환식
accumulate String X 미터링 정보에 대해서 누적 단계에 적용하는 계산식 또는 변환식
aggregate String X 미터링 정보에 대해서 집계 단계에 적용하는 계산식 또는 변환식
summarize String X 미터링 정보를 보고할 때 적용하는 계산식 또는 변환식
title String X API 미터링 제목

####2. 미터링 정책 예제

{
  "plan_id": "basic-object-storage",
  "measures": [
    {
      "name": "storage",
      "unit": "BYTE"
    },
    {
      "name": "api_calls",
      "units": "CALL"
    }
  ],
  "metrics": [
    {
      "name": "storage",
      "unit": "GIGABYTE",
      "meter": "(m) => m.storage / 1073741824",
      "accumulate": "(a, qty) => Math.max(a, qty)"
    },
    {
      "name": "thousand_api_calls",
      "unit": "THOUSAND_CALLS",
      "meter": "(m) => m.light_api_calls / 1000",
      "accumulate": "(a, qty) => a ? a + qty : qty",
      "aggregate": "(a, qty) => a ? a + qty : qty",
      "summarize": "(t, qty) => qty"
    }
  ]
}

###

2.5.2. 등급 정책

등급 정책이란 각 서비스의 사용 가중치를 정의한 JSON 형식의 오브젝트이다. 서비스 제공자는 등급 정책 스키마에 맞춰 서비스에 대한 정책을 개발한다.

####1. 등급 정책 스키마

항목명 유형 필수 설명
plan_id String O API 등급 Plan ID
metrics Array 최소 하나 등급 정책 목록
name String O 등급 정의 대상 명
rate String X 가중치 계산식 또는 변환식
charge String X 사용량에 대한 과금 계산식 또는 변환식
title String X 등급 정책 명

####2. 등급 정책 예제

{
  "plan_id": "object-rating-plan",
  "metrics": [
    {
      "name": "storage"
    },
    {
      "name": "thousand_api_calls",
      "rate": "(p, qty) => p ? p * qty : 0",
      "charge": "(t, cost) => cost"
    }
  ]
}

###

2.5.3. 과금 정책

과금 정책이란 각 서비스에 대한 사용 단가를 정의한 JSON 형식의 오브젝트이다. 서비스 제공자는 과금 정책 스키마에 맞춰 서비스에 대한 정책을 개발한다.

####1. 과금 정책 스키마

항목명 유형 필수 설명
plan_id String O API 과금 Plan ID
metrics Array 최소 하나 과금 정책 목록
name String O 과금 대상 명
price Array 최소 하나 과금 정책 상세
country String O 서비스 사용 단가에 적용할 통화
price Number O 서비스 사용 단가
title String X 과금 정책 제목

####2. 과금 정책 예제

{
  "plan_id": "object-pricing-basic",
  "metrics": [
    {
      "name": "storage",
      "prices": [
        {
          "country": "USA",
          "price": 1
        },
        {
          "country": "EUR",
          "price": 0.7523
        },
        {
          "country": "CAN",
          "price": 1.06
        }
      ]
    },
    {
      "name": "thousand_api_calls",
      "prices": [
        {
          "country": "USA",
          "price": 0.03
        },
        {
          "country": "EUR",
          "price": 0.0226
        },
        {
          "country": "CAN",
          "price": 0.0317
        }
      ]
    }
  ]
}

###

2.5.4. 정책 등록 정책은 2가지 방식 중 하나의 방법으로 CF-ABACUS에 등록할 수 있다.

####1. js 파일을 등록하는 방식

작성한 정책을 다음의 디렉토리에 저장한 후, CF에 CF-ABACUS를 배포 또는 재 배포 한다.

  • 미터링 정책의 경우

    cf-abacus/lib/plugins/provisioning/src/plans/metering
    
  • 등급 정책의 경우

    cf-abacus/lib/plugins/provisioning/src/plans/pricing
    
  • 과금 정책의 경우

    cf-abacus/lib/plugins/provisioning/src/plans/rating
    

####2. DB에 등록하는 방식

작성한 정책을 curl 등을 이용해 DB에 저장하는 방식으로 CF-ABACUS를 재배포할 필요는 없다. 정책 등록 시, 정책 ID는 고유해야 한다.

  • 미터링 정책의 경우

    POST /v1/metering/plans/:metering_plan_id
    
    ## 예제
    $ curl -k -X POST 'http://abacus-provisioning-plugin.bosh-lite.com/v1/metering/plans/sample-linux-container' \
    	 -H "Content-Type: application/json" \
    	 -d '{"plan_id":"sample-linux-container","measures":[{"name":"current_instance_memory","unit":"GIGABYTE"},{"name":"current_running_instances","unit":"NUMBER"},{"name":"previous_instance_memory","unit":"GIGABYTE"},{"name":"previous_running_instances","unit":"NUMBER"}],"metrics":[{"name":"memory","unit":"GIGABYTE","type":"time-based","meter":"((m)=>({previous_consuming:newBigNumber(m.previous_instance_memory||0).div(1073741824).mul(m.previous_running_instances||0).mul(-1).toNumber(),consuming:newBigNumber(m.current_instance_memory||0).div(1073741824).mul(m.current_running_instances||0).toNumber()})).toString()","accumulate":"((a,qty,start,end,from,to,twCell)=>{if(end<from||end>=to)returnnull;constpast=from-start;constfuture=to-start;consttd=past+future;return{consuming:a&&a.since>start?a.consuming:qty.consuming,consumed:newBigNumber(qty.consuming).mul(td).add(newBigNumber(qty.previous_consuming).mul(td)).add(a?a.consumed:0).toNumber(),since:a&&a.since>start?a.since:start};}).toString()","aggregate":"((a,prev,curr,aggTwCell,accTwCell)=>{if(!curr)returna;constconsuming=newBigNumber(curr.consuming).sub(prev?prev.consuming:0);constconsumed=newBigNumber(curr.consumed).sub(prev?prev.consumed:0);return{consuming:consuming.add(a?a.consuming:0).toNumber(),consumed:consumed.add(a?a.consumed:0).toNumber()};}).toString()","summarize":"((t,qty,from,to)=>{if(!qty)return0;constrt=Math.min(t,to?to:t);constpast=from-rt;constfuture=to-rt;consttd=past+future;constconsumed=newBigNumber(qty.consuming).mul(-1).mul(td).toNumber();returnnewBigNumber(qty.consumed).add(consumed).div(2).div(3600000).toNumber();}).toString()"}]}' \
    	-H "Authorization: $(cf oauth-token | grep bearer)"
    
  • 등급 정책의 경우

    POST /v1/rating/plans/:rating_plan_id
    
    ## 예제
    $ curl -k -X POST 'http://abacus-provisioning-plugin.bosh-lite.com/v1/rating/plans/linux-rating-sample' \
    	 -H "Content-Type: application/json" \
         -d '{"plan_id":"linux-rating-sample","metrics":[{"name":"memory","rate":"((price,qty)=>({price:price,consuming:qty.consuming,consumed:qty.consumed})).toString(),charge:((t,qty,from,to)=>{if(!qty)return0;constrt=Math.min(t,to?to:t);constpast=from-rt;constfuture=to-rt;consttd=past+future;constconsumed=newBigNumber(qty.consuming).mul(-1).mul(td).toNumber();constgbhour=newBigNumber(qty.consumed).add(consumed).div(2).div(3600000).toNumber();returnnewBigNumber(gbhour).mul(qty.price).toNumber();}).toString()"}]}' \
    	 -H "Authorization: $(cf oauth-token | grep bearer)"
    
  • 과금 정책의 경우

    POST /v1/pricing/plans/:pricing_plan_id
    
    ## 예제
    $ curl -k -X POST 'http://abacus-provisioning-plugin.bosh-lite.com/v1/pricing/plans/linux-pricing-sample' \
    	 -H "Content-Type: application/json" \
    	 -d '{"plan_id":"linux-pricing-sample","metrics":[{"name":"memory","prices":[{"country":"USA","price":0.00014}]}]}' \
    	-H "Authorization: $(cf oauth-token | grep bearer)"
    

##

2.6. 배포

파스-타 플랫폼에 애플리케이션을 배포하면 배포한 애플리케이션과 파스-타 플랫폼이 제공하는 서비스를 연결하여 사용할 수 있다. 파스-타 플랫폼상에서 실행을 해야만 파스-타 플랫폼의 애플리케이션 환경변수에 접근하여 서비스에 접속할 수 있다.

###

2.6.1 파스-타 플랫폼 로그인

아래의 과정을 수행하기 위해서 파스-타 플랫폼에 로그인

$ cf api --skip-ssl-validationhttps://api.<파스-타 도메인> #파스-타 플랫폼TARGET지정

$ cf login -u <user name> -o <org name> -s <space name>#로그인 요청

###

2.6.2. API 서비스 브로커 생성

애플리케이션에서 사용할 서비스를 파스-타 플랫폼을 통하여 생성한다. 별도의 서비스 설치과정 없이 생성할 수 있으며, 애플리케이션과 바인딩과정을 통해 접속정보를 얻을 수 있다.

  • 서비스 생성 (cf marketplace 명령을 통해 서비스 목록과 각 서비스의 플랜을 조회할 수 있다.)

    ##서비스 브로커 CF 배포
    $ cd <샘플 서비스 브로커 경로>/sample_api_java_broker
    $ cf push
    
    
    ##서비스 브로커 생성
    $ cf create-service-broker <서비스 브로커 명> <인증ID> <인증Password> <서비스 브로커 주소>
    
    예)
    $ cf create-service-broker sample-api-broker admin cloudfoundry http://sample-api-java-broker.bosh-lite.com
    
    ##서비스 브로커 확인
    $ cf service-brokers
    Getting service brokers as admin...
    
    name url
    sample-api-broker http://sample-api-java-broker.bosh-lite.com
    
    ##서비스 카탈로그 확인
    $ cf service-access
    Getting service access as admin...
    broker: sample-api-broker
    service plan access orgs
    standard_obejct_storage_light_api_calls standard none
    standard_obejct_storage_heavy_api_calls basic none
    
    ##등록한 서비스 접근 허용
    $ cf enable-service-access <서비스명> -p <플랜 명>
    
      예)
    $ cf enable-service-access standard_obejct_storage_light_api_calls -p standard
    

###

2.6.3. API 서비스 애플리케이션 배포 및 서비스 등록

API 서비스 애플리케이션을 파스-타 플랫폼에 배포한다. 서비스 등록한 API는 다른 애플리케이션과 바인다 하여 API 서비스를 할 수 있다.

  1. 애플리케이션 배포

    • gradle build -x test 명령으로 빌드 한다.

    • cf push 명령으로 배포한다. 별도의 값을 넣지않으면 manifest.yml의 설정을 사용한다

      ## API 서비스 배포
      $ cd <샘플api서비스 경로>/sample_api_java_service
      ## gradle 빌드
      $ gradle build -x test
      :compileJava
      :processResources
      :classes
      :findMainClass
      :jar
      :bootRepackage
      :assemble
      :check
      :build
      
      BUILD SUCCESSFUL
      
      Total time: 13.426 secs
      $ cf push
      
      ##서비스 생성
      $ cf create-service <서비스명> <플랜 명> <서비스 인스턴스 명>
      예)
      $ cf create-service standard_obejct_storage_light_api_calls standard sampleNodejslightCallApi
      
      ##서비스 확인
      $ cf services
      Getting services in org real / space ops as admin...
      OK
      
      name service plan bound apps last operation
      sampleNodejslightCallApi standard_obejct_storage_light_api_calls standard create succeeded
      

###

2.6.4. API 서비스 연동 샘플 애플리케이션 배포 및 서비스 연결

애플리케이션과 서비스를 연결하는 과정을 '바인드(bind)라고 하며, 이 과정을 통해 서비스에 접근할 수 있는 접속정보를 생성한다.

  • 애플리케이션과 서비스 연결

    ## API 서비스 연동 샘플 애플리케이션 배포
    $ cd <샘플 애플리케이션 경로>/sample_api_node_caller
    $ npm install && npm run babel && npm run cfpack && ./cfpush.sh
    
    ## 서비스 바인드
    $ cf bind-service <APP_NAME> <SERVICE_INSTANCE> -c <PARAMETERS_AS_JSON>
    
    예) 
    $ cf bind-service sample-api-node-caller sampleNodejslightCallApi -c '{"serviceKey": "cloudfoundry"}'
    
    ## 서비스 연결 확인
    $ cf services
    Getting services in org real / space ops as admin...
    OK
    
    name                       service                                   plan       bound apps               last operation   
    sampleNodejslightCallApi   standard_obejct_storage_light_api_calls   standard   sample-api-node-caller   create succeeded
    
    ## 애플리케이션 실행
    $ cf start <APP_NAME>
    
    예)
    $ cf start sample-api-node-caller
    
    ## 형상 확인
    $ cf a
    Getting apps in org real / space ops as admin...
    OK
    
    name                      requested state   instances   memory   disk   urls   
    sample-api-node-service   started           1/1         512M     512M   sample-api-node-service.bosh-lite.com   
    sample-api-java-broker    started           1/1         512M     1G     sample-api-java-broker.bosh-lite.com   
    sample-api-node-caller    started           1/1         512M     512M   sample-api-node-caller.bosh-lite.com
    

##

2.7. API 및 CF-Abacus 연동 테스트

API 연동 샘플 애플리케이션의 url을 통해 웹 브라우저에서 접속하면 API 연동 및 API 사용량에 대한 CF-Abacus 연동 테스트를 진행 할 수 있다.

  1. CF-Abacus 연동 확인

    ## 조직 guid 확인
    $ cf org <샘플 애플리케이션을 배포한 조직> --guid
    
    예)
    $ cf org real --guid
    877d01b2-d177-4209-95b0-00de794d9bba
    
    ## 샘플 애플리케이션 guid 확인
    $ cf env <샘플 애플리케이션 명>
    예)
    $ cf env sample-api-node-caller
    Getting env variables for app sample-api-node-caller in org real / space ops as admin...
    OK
    
    <<중략>>
    {
     "VCAP_APPLICATION": {
      "application_id": "58872d8a-edfc-44df-97f0-df67cf9033a7",
      "application_name": "sample-api-node-caller",
      "application_uris": [
       "sample-api-node-caller.bosh-lite.com"
      ],
      "application_version": "55678102-584c-4fca-8304-82f727506b1d",
      "limits": {
       "disk": 512,
       "fds": 16384,
       "mem": 512
      },
      "name": "sample-api-node-caller",
      "space_id": "2ce08996-f463-406c-a971-adbbaf4e4ca5",
      "space_name": "ops",
      "uris": [
       "sample-api-node-caller.bosh-lite.com"
      ],
      "users": null,
      "version": "55678102-584c-4fca-8304-82f727506b1d"
     }
    }
    
    <<후략>> 
    
    ## API 사용량 확인
    $ curl 'http://abacus-usage-reporting.<파스-타 도메인>/v1/metering/organizations/<샘플 애플리케이션을 배포한 조직>/aggregated/usage'
    
    예)
    $ curl 'http://abacus-usage-reporting.bosh-lite.com/v1/metering/organizations/877d01b2-d177-4209-95b0-00de794d9bba/aggregated/usage'
    

##

2.8. 샘플코드

샘플코드는 아래의 사이트에 다운로드 할 수 있다.

다운로드