yoonyoul-blog
yoonyoul-blog
Ryukato
7 posts
Don't wanna be here? Send us removal request.
yoonyoul-blog · 8 years ago
Text
ELK 정리
ELK (Elasticsearch, Logstash, Kibana)
ELK (Elasticsearch, Logstash, Kibana)를 통해 애플리케이션 혹은 시스템의 로그들을 수집하여 검색 및 분석을 할 수 있는 환경을 구성할 수 있다.
Logstash
파일, TCP, HTTP 등의 다양한 입력 소스를 통해들어 오는 데이터를 수집할 수 있는 파이프라인을 제공한다. 들어오는 데이터를 가공할 수 있으며, Elasticsearch로 데이터를 전송할 수 있다.
설치
사전 조건
Logstash 설치를 위해선 Java 8이 설치되어 있어야 한다. Java 8설치 유무를 확인하기 위해 아래의 명령어를 실행한다. 만약 Java 8이 설치 되어 있지 않다면, Java 8 다운로드페이지를 통해 다운로드 하여 설치한다.
java version 확인
java -version
java version 확인 결과
java version "1.8.0_65" Java(TM) SE Runtime Environment (build 1.8.0_65-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
(*위의 Java version 값은 로컬 환경에 설치된 Java에 따라 다를 수 있다.)
설치 및 설정
Logstash 설치를 위한 설치 파일을 직접 다운로드할 수 있다.
Binary 파일을 통한 설치
윈도우 환경에 설치를 진행하기 위한 ZIP 파일을 다운로드하여 설치하고자 하는 경로에 압축을 풀면 된다.
리눅스 환경
유닉스 환경
맥 환경
Homebrew를 통해 Logstash를 설치 할 수 있다. 설치를 위해 아래의 명령어를 실행한다.
brew install logstash
구성
(* 출처: www.elastic.co)
위의 이미지에서 볼 수 있듯이 Logstash는 기본적으로 input, filter 그리고 out이라는 구성요소를 가지고 있다. Input과 output은 필수 요소이고 filter는 필요에 따라 구성할 수 있다. 아래의 명령어를 통해 콘솔을 통한 입/출력 하는 가장 기본적인 구성으로 Logstash를 실행할 수 있다.
bin/logstash -e 'input {stdin { } } output { stdout { } } '
(* 위에서 사용은 -e 옵션은 Logstash의 설정을 명령어를 통해 직접 입력 할 수 있게 해준다.)
위의 명령어를 실행한 후, 명령어 창에 hello world 를 입력하면 입력한 hello world 가 바로 출력되는 것을 볼 수 있다.
기본 설정 구성
위에서 언급한 것처럼 Logstash 설정 파일은 input, filter 그리고 output으로 구성이 되어 있다. 아래는 템플릿으로 사용 가능한 설정 파일 내용이다.
# The # character at the beginning of a line indicates a comment. Use # comments to describe your configuration. input { } # The filter part of this file is commented out to indicate that it is # optional. # filter { # # } output { }
Input
File
Logstash가 설치된 로컬 환경에 존재하는 파일의 내용을 input으로 설정하여 처리할 수 있다. 아래는 로컬 파일을 처리하기 위한 설정의 예이다.
input { file { path => "/tmp/access_log" start_position => "beginning" } }
FileBeat를 통한 File 처리
Filebeat를 통해 pipeline을 구축할 수 있다. 다만 Beats input plugin이 먼저 설치되어 있어야 한다. (* Beats input plugin은 Logstash 설치 시 기본으로 함께 설치된다. )
Note: 일반적으로 Filebeat는 Logstash가 설치된 machine과는 다른 machine에 설치하여 실행한다.
Filebeat 설치 및 설정은 Filebeat를 참조하면 된다.
Filebeat 설치 후, 아래와 같은 설정을 통해 Logstash와 연동 시킬 수 있다.
Filebeat 설정
filebeat.prospectors: - type: log paths: - /path/to/file/logstash-tutorial.log output.logstash: hosts: ["localhost:5044"]
Logstash Filebeat input 설정
아래의 내용은 Logstash에서 Filebeat의 내용을 input으로 설정하는 것으로 Beats input plugin을 사용한다.
input { beats { port => "5044" } } filter { json { source => "message" target => "message" } } output { stdout { codec => rubydebug } elasticsearch { hosts => ["localhost:9200"] index => "application-log-%{+YYYY.MM.dd}" workers => 1 user => elastic password => changeme } }
filter 설정
filter { json { source => "message" target => "message" } }
위와 같은 filter 설정에 대해 좀 더 설명하면, 애플리케이션에서 남기는 로그 형태가 JSON 형태일때 Filebeat는 해당 로그를 JSON으로 인식하여 읽어 들이지 않는다. 즉, 일반 문자열로 읽어 들이기 때문에 로그 내용이 아래와 같을 경우 큰 따옴표(")를 escaping 처리한다. 따라서 Logstash의 input으로 해당 로그 내용이 전달이 되면 json filter를 통해 정상적인 JSON 형식으로 만들어 주어야 한다.
애플리케이션에서 남긴 로그 예제
... "{ "method ": "GET ", "userAgent ": { "operatingSystem ": "MAC_OS_X ", "browser ": "SAFARI " }, "@timestamp ": "2017 - 11 - 22 T18: 40: 41.639 + 09: 00 ", "instanceId ": "localhost" }" ...
Filebeat가 읽어 들인 로그 예제
... "{ \"method \":\"GET \", \"userAgent \": { \"operatingSystem \":\"MAC_OS_X \", \"browser \":\"SAFARI \" }, \"@timestamp \":\"2017 - 11 - 22 T18: 40: 41.639 + 09: 00 \", \"instanceId \":\"localhost\" }" ...
Logstash json filter 처리 후 내용 예제
... "message" => { "instanceId" => "localhost", "method" => "GET", "userAgent" => { "operatingSystem" => "MAC_OS_X", "browser" => "SAFARI" } } ...
위와 같은 설정 방법이 아닌 다른 방법으로는 Filebeat의 processor를 사용하면 된다. 아래는 Filebeat에서 읽어 들인 내용을 JSON으로 처리하기 위한 예제 설정 내용이다. 아래의 설정에 추가적으로 사용된 json 옵션을 살펴보면 된다.
filebeat.prospectors: - type: log paths: - /path/to/file/logstash-tutorial.log json.keys_under_root: true json.overwrite_keys: true processors: -decode_json_fields: fields: ["message"] output: logstash: hosts: ["localhost:5044"]
TCP, UDP
Logstash는 TCP 혹은 UDP을 input으로 사용할 수 있다. 아래는 TCP, UDP를 사용한 예제 설정이며 Logstash는 9600 포트를 통해 TCP, UDP input을 처리하도록 설정된다.
tcp { port => 9600 type => syslog } udp { port => 9600 type => syslog }
input data의 형식이 JSON 형식일 경우 아래와 같이 codec을 추가하여 준다.
tcp { port => 9600 type => syslog codec => json { charset => "UTF-8" } } udp { port => 9600 type => syslog codec => json { charset => "UTF-8" } }
HTTP
Logstash의 http plugin을 사용하여 HTTP(S)를 통해 단일 혹은 multiline을 입력 받을 수 있도록 할 수 있다. Http를 통해 들어 오는 내용의 Content-Type에 해당 하는 codec이 사용된다. 예를 들어 Content-Type이 application/json일 경우, json codec이 사용된다. 반면 다른 Content-Type에 대해선 plain codec이 사용된다.
HTTP의 기본 인증 표준을 지원하며, SSL 설정에 따라 https를 통해 들어 오는 데이터를 처리할 수 있다. 인증서 설정은 Java Keystore format을 통해 가능하다.
사용 가능한 옵션들은 Http Input Configuration Options를 참고하면 된다.
간단한 설정 예제는 아래와 같다.
input { http { host => "localhost" port => 9600 user => "elastic" password => "changeme" } }
Filter
Logstash의 pipeline을 통해 읽어 들인 로그에서 원하는 정보를 뽑아내거나, 해당 로그를 원하는 형태로 만들기 위해선 filter를 사용해야 한다. Logstash는 다양한 filter plugin을 지원하면 이중 하나인 grok filter를 살펴본다.
Grok filter를 통해 들어오는 로그 데이터의 일정한 패턴을 분석하여 구조화된 그리고 검색 가능한 데이터로 변경할 수 있다. Grok filter는 기본적으로 정규식을 사용하여 표현 할 수 있다. Logstash는 다양한 filter pattern을 기본 제공하고 있으며, https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns 이곳에서 원하는 filter를 검색하여 사용할 수 있다.
Grok Basics
Grok pattern의 사용 문법은 %{SYNTAX:SEMANTIC} 이며, SYNTAX는 로그의 내용 중 뽑아내고자 하는 부분의 패턴을 의미한다. 예를 들어 로그 내용 중 3.44라는 숫자 값을 뽑아낼려면 NUMBER pattern을 사용하면 된다. 그리고 SEMANTIC은 SYNTAX를 통해 일치된 부분을 식별하기 위한 식별자를 의미한다. 예를 들어 이미 언급한 NUMBER pattern을 통해 뽑아��� 3.44가 duration을 의미한다면 SEMANTIC에 duration을 설정하면 된다.
%{NUMBER: duration}
정규식 표현
Grok에서 사용하는 정규식 표현 라이브러리는 Oniguruma로 지원 가능한 상세 표현식은 Oniguruma Site에서 확인 가능하다.
사용자 정의 패턴
Logstash가 제공하지 않는 패턴을 사용하기 위해 아래와 같이 직접 패턴을 정의할 수 있다.
(?<queue_id>[0-9A-F]{10,11})
또한 자주 사용하는 패턴의 경우 다음의 단계를 통해 저장해서 재 사용 할 수 있다. * patterns라는 이름으로 폴더를 생성한다. * 사용할 pattern을 포함한 파일명을 지정하여 저장한다. * 해당 파일에 사용할 패턴을 작성하여 저장한다.
# contents of ./patterns/postfix: POSTFIX_QUEUEID [0-9A-F]{10,11}
위의 패턴을 사용한 예제는 아래와 같다.
로그 내용
Jan 1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=<[email protected]>
filter 설정 내용
filter { grok { patterns_dir => ["./patterns"] match => { "message" => "%{SYSLOGBASE} %{POSTFIX_QUEUEID:queue_id}: %{GREEDYDATA:syslog_message}" } } }
결과 항목
timestamp: Jan 1 06:25:43
logsource: mailserver14
program: postfix/cleanup
pid: 21403
queue_id: BEF25A72965
syslog_message: [email protected]
Grok Filter Configuration Options
Grok filter 설정 시 사용 가능한 상세 옵션은 상세 옵션을 통해 확인 가능하다.
Output
//TODO
Elasticsearch
//TODO
Kibana
//TODO
Filebeat
Filebeat는 파일에 담겨 있는 로그 내용을 수집하여 원격의 Logstash로 전송하기 위한 도구 이다. Logstash를 설치하게 되면 Filebeat와 연동 가능한 Beats input plugin이 기본적으로 함께 설치되어 있다. Logstash는 Beats input plugin를 통해 Elastic Beats framework으로부터의 이벤트를 수신하게 된다.
설치
윈도우에 설치
윈도우에 Filebeat를 설치하기 위해선 다운로드 페이지에서 ZIP 파일을 다운 받는다.
다운로드한 압축 파일을 C:\Program Files 하위에 압축을 풀어 준다.
폴더 명을 Filebeat로 변경한다.
PowerShell을 관리자 권한으로 실행한다.
Filebeat를 윈도우 서비스로 실행하기 위해 아래의 명령어를 PowerShell창에 입력하고 실행한다.
PS > cd 'C:\Program Files\Filebeat' PS C:\Program Files\Filebeat> .\install-service-filebeat.ps1
윈도우이외의 환경에 설치
deb
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.0.0-amd64.deb sudo dpkg -i filebeat-6.0.0-amd64.deb
rpm
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.0.0-x86_64.rpm sudo rpm -vi filebeat-6.0.0-x86_64.rpm
mac
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.0.0-darwin-x86_64.tar.gz tar xzvf filebeat-6.0.0-darwin-x86_64.tar.gz
(* homebrew를 통해 Filebeat를 설치 할 경우, /usr/local/Cellar/filebeat/6.0.0/.bottle/etc/filebeat 밑dp filebeat.reference.yml 파일과 filebeat.yml 파일이 존재한다.)
docker
docker pull docker.elastic.co/beats/filebeat:6.0.0
설정
Filebeat는 filebeat.yml파일을 통해 설정 내용을 관리한다. Filebeat는 참조용 설정 파일인 filebeat.reference.yml을 제공한다. 각 환경별 filebeat.yml의 위치는 아래와 같다. * rpm, deb : /etc/filebeat/filebeat.yml * Docker: /usr/share/filebeat/filebeat.yml * Mac, Windows: 압축 푼 경로내에 위치.
샘플 설정 파일
filebeat.prospectors: - type: log enabled: true paths: - /var/log/*.log #- c:\programdata\elasticsearch\logs\*
Filebeat 설정은 다음과 같은 단계를 거쳐 진행하면 된다.
로그 파일의 위치를 지정한다.
Filebeat 설정의 가장 기본적인 단계로 하나의 prospector를 다음과 같이 정의 할 수 있다.
filebeat.prospectores: - type: log enabled: true paths: - /var/log/*.log output.logstash: hosts: ["localhost:5044"]
위에 설정한 내용처럼 설정된 prospector는 /var/log 하위의 모든 log 확장자를 가진 파일의 내용을 수집하게 된다. 단, 현재는 디렉토리의 모든 하위 디렉토리에있는 모든 파일을 재귀 적으로 가져올 수 없다.
output 설정
Logstash로 보내기
아래와 같이 hosts 옵션을 통해 다수의 Logstash 서버를 지정할 수 있으며 Beats connection을 위해 사용되는 5044 포트를 설정한다. 아래와 같이 설정하여 운영하기 위해선 Elasticsearch에 직접 index template을 생성해야 한다.
output.logstash: hosts: ["localhost:5044"]
Elasticsearch로 바로 보내기
Filebeat가 수집한 내용을 Elasticsearch로 바로 보낼 수 있는데, 이를 위한 설정은 아래와 같다. Elasticsearch에 보안 목적의 사용자 계정과 비밀번호가 설정되어 있는 경우에는 username, password를 꼭 설정해야 한다.
output.elasticsearch: hosts: ["localhost:9200"] username: elastic password: changeme
Kibana에 dashboard 설정
Kibana에 Filebeat가 제공하는 샘플 dashboard를 다음과 같이 설정할 수 있다.
setup.kibana: host: "localhost:5601" username: elastic password: changeme
기타 사항
Logstash의 geoip plugin 사용 시, client의 ip가 127.0.0.1인 경우, _geoip_lookup_failure가 발생한다.
참고 자료
Filebeat
filebeat-reference-yml
0 notes
yoonyoul-blog · 8 years ago
Text
Spring Data Repository
Spring Data Repository
Repository는 data를 저장하는 역활을 하는 모든 객체를 추상화한 단어로 생각하면 된다. 실제 구체적인 예는 아래와 같다.
DBMS - database
File System - File
NoSQL - database
그리고 Repository에 저장될 객체의 유형(타입)은 보통 Generic을 사용하여 선언한다. 아래의 예제에서 Generic 선언은 <user>이다. 예) UserRepository<user>
위의 예를 풀어서 말하면, User타입의 객체를 저장하는 UserRepository로 말할 수 있다.
Spring data와 MongoDB
Spring data는 MongoDB와 연동하기 위해 MongoRepository interface를 제공한다. 이에 대한 하위 구현 class는 아래와 같다.
org.springframework.data.mongodb.repository.support.SimpleMongoRepository: MongoRepository의 기본 구현 클래스로 save, insert, findAll 메서드등 에 대한 구현을 모두 포함하고 있다. 직접 Repository interface를 선언하여 사용할 경우, SimpleMongoRepository가 proxy객체로 사용된다.
How to use
Basic use
아래와 같이 직접 Repository를 정의하여 사용할 수 있다. 아래의 예제에서 MongoRepository<user string> 에 사용된 첫번째(User) generic type은 repository가 저장할 객체의 타입을 말하며, 두번째(String)은 User 객체의 id값의 타입을 말한다.
@RepositoryRestResource( collectionResourceRel = "services", // collection을 mapping한다. path = "services" ) public interface UserRepository extends MongoRepository<user string> { public User findById(String id); public Page<user> findByNameLike(String name); }
위와 같이 MongoRepository를 상속받는 interface만을 선언해 놓으면 spring data에서 나머지는 알아서 처리해준다. 즉, 다른 객체에서 UserRepository. findByNameLike를 호출하게 되면 주어진 name과 유사한 User들을 반환해 준다. interface의 구현 class를 직접 작성하지 않아도 된다는 것이다.
Custom Use#1
Basic use 와 같이 spring data에서 제공하는 기본기능만을 사용해도 되지만, 세상이 늘 그렇게 쉽지 많은 않다. 예를 들어 사용자의 나이를 기준으로 몇세 이하, 몇세 이상등의 특정 use case에 맞는 조회를 해야 하는 경우가 실제에선 비일비재하게 생기게 마련이다.
이런 경우 아래의 UserRepositoryCustom 같이 특정 use case에 필요한 메서드를 포함하고 있는 interface를 선언하고 이를 구현하는 구현 class를 선언하면 된다. 구현 class의 이름에 유의해야 한다. 반드시 MongoRepository를 상속한 repository의 이름(UserRepository) + "Impl"로 선언해야 spring data에서 자동으로 인식한다.
아래이 예제를 보자
UserRepositoryCustom
public interface UserRepositoryCustom { Page<user> findByAgeLessThanEqual(int age); Page<user> findByAgeGreaterThanEqual(int age); }
UserRepositoryImpl
public class UserRepositoryImpl implements UserRepositoryCustom { @Autowired MongoTemplate mongoTemplate; Page<user> findByAgeLessThanEqual(int age) { Pageable pageable = new PageRequest(0, 100); //pageNo, rowCount per page Query query = new Query( Criteria.where("age").lte(age) //lte -> less then equal ); long totalCount = mongoTemplate.count(query, User.class); List<user> userList = mongoTemplate.find(query, User.class); return new PageImpl(userList, pageable, totalCount); } }
마지막으로 UserRepository를 UserRepositoryCustom의 하위interface로 만들어 준다.
@RepositoryRestResource( collectionResourceRel = "services", // collection을 mapping한다. path = "services" ) public interface UserRepository extends MongoRepository<user string>, UserRepositoryCustom { public User findById(String id); public Page<user> findByNameLike(String name); }
Custom Use#2
Custom Use#1는 실제로 Custom interface를 사용하지 않아도 된다. 왜냐면 age의 경우 보통 원시(primitive)타입이기때문이다. custom interface없이 그냥 findByAgeLessThan이렇게 UserRepository에 선언만 해줘도 spring data가 알아서 해준다.
쿼리 메서드 선언 유형 확인
하지만 세상이 그렇게 쉬운가? 클래스를 정의할때 원시 타입의 변수만 있으면 얼마나 좋겠나. 또 다른 클래스로 선언된 객체를 참조하는 참조 타입의 변수를 사용할 경우는 위에서 한 것처럼 custom interface를 선언하여 사용해야 한다.
예를 들어 User클래스의 주소가 다음과 같은 class로 정의되어 있다고 하자.
//편의상 getter/setter는 생략한다. public class Address { private String zipCode; private String address1; private String address2; }
public class User { ... private Address address; ... }
위와 같이 정의된 상태에서 주소가 서울인 사용자만을 조회한다고 하면 어떻게 해야 할까? findByAddressLike로 하면될까? 한번 해보���길.
그럼 위에서 사용한 custom interface를 제대로 사용해 보자. 현내용에 집중하기 위해 기존 메서드는 삭제하였다.
UserRepositoryCustom
public interface UserRepositoryCustom { Page<user> findByAddressLike(String partOfAddress, Pageable pageable); }
UserRepositoryImpl
public class UserRepositoryImpl implements UserRepositoryCustom { @Autowired MongoTemplate mongoTemplate; Page<user> findByAddressLike(String partOfAddress, Pageable pageable) { //like와 같은 효과를 내기 위해 정규식을 사용. Pattern addressPattern = Pattern.compile("^.*"+ partOfAddress + ".*"); Query query = new Query( Criteria.where("address.address1") .is(addressPattern) .or(address.address2) .is(addressPattern) ); } }
참조타입의 변수를 사용할 경우에는 위의 예제처럼 custom interface를 사용해서 데이터를 조회하면 될 것이다.
0 notes
yoonyoul-blog · 8 years ago
Text
Spring Actuator 정리
Spring Actuator
Spring actuator는 개발 혹은 실 운영에 배포된 애플리케이션을 모니터링하고 관리하기 위한 HTTP 기반의 endpoints, JMX 혹은 원격 쉘(Shell 혹은 Telnet)을 제공한다. Spring boot 기반의 애플리케이션에 actuator 라이브러리(의존성)을 추가하면 auditing, health 그리고 metrics를 파악할 수 있는 기능(endpoints)들이 자동으로 애플리케이션에 추가된다.
단 actuator의 HTTP endpoint들은 spring-mvc 기반의 애플리케이션에서만 작동한다. 즉, Spring MVC를 사용하지 않는 다면 Jersey 기반의 애플리케이션에선 actuator의 HTTP endpoint들을 사용할 수 없다.
Jersery와 Spring MVC 사용하기
Actuator의 정의
Actuator는 제조에서 사용하는 용어인데, 제조쪽에서 어떤 물건이나 다른 장비 등을 움직이거나 제어하는데 사용하는 장비를 일컷는다. Actuator는 작은 변화를 주어 많은 양의 제어 모션들을 생성할 수 있다.
Maven: actuator dependency in spring-boot
<dependencies><dependency><groupid>org.springframework.boot</groupid><artifactid>spring-boot-starter-actuator</artifactid></dependency></dependencies>
Gradle: actuator dependency in spring-boot
dependencies { compile("org.springframework.boot:spring-boot-starter-actuator") }
Endpoints
Actuator는 애플리케이션을 제어하고 모니터링 할 수 있는 endpoints라고 하는 HTTP API를 제공한다. 기본적으로 제공되는 endpoint들이 있고, 여기에 직접 정의한 endpoint들을 추가 할 수 있다.
ID Description Sensitive Default example actuator 다른 endpoints들에 대한 정보를 hypermedia-based의 표현으로 제공한다. 단 HATEOAS 라이브러리(spring-boot-starter-hateoas)를 추가해야 한다. true /{actuator management context}/actuator auditevents 애플리케이션의 AUTHENTICATION_SUCCESS과 같은 audit events 정보를 제공한다. true /{actuator management context}/auditevents autoconfig 애플리케이션의 자동 설정 보고서를 보여주며, 자동 설정 내용 중 이미 설정된 것과 그렇지 않은 것을 보여준다. true /{actuator management context}/autoconfig beans 애플리케이션의 모든 bean 정보들을 제공한다. true /{actuator management context}/beans configprops 모든 @ConfigurationProperties들의 조합을 제공한다. true /{actuator management context}/configprops dump Thread dump 파일을 제공한다. true /{actuator management context}/dump env profiles과 systemEnvironment등과 같은 Spring의 ConfigurableEnvironment 속성들을 제공한다. true /{actuator management context}/env flyway Flyway를 통해 수행된 database migration 정보를 제공한다. true /{actuator management context}/flyway liquibase Liquibase를 통해 수행된 database migration 정보를 제공한다. true /{actuator management context}/liquibase health 애플리케이션의 상태(예, UP, DOWN등)에 대한 정보를 표시한다. security를 적용한 상태에서 인증되지 않은 사용자의 요청에는 UP, DOWN 정도의 상태만 표시되며, 인증된 사용자에게는 보다 더 상세한 정보를 제공한다. false /{actuator management context}/health info 애플리케이션의 app, build, git과 같은 임의의 정보를 제공한다. false /{actuator management context}/info loggers 애플리케이션에 설정된 logger들의 정보를 제공하며, 특정 로거의 로그 레벨등을 변경할 수 도 있다. 단 logback.xml이 classpath에 존재해야 한다. true /{actuator management context}/loggers, /{actuator management context}/loggers/{loggername} metrics 로딩된 class 개수, 힙 메모리, 쓰레드 등의 정보를 종합해서 보여주는 metrics 정보를 제공한다. true /{actuator management context}/metrics mappings @RequestMapping을 통해 설정된 API 정보의 조합 목록을 제공한다. true /{actuator management context}/mappings shutdown 실행 중인 애플리케이션을 정상적으로 종료할 수 있게 해준다. 기본적으로 shutdown endpoint는 비활성화 되어 있다. true /{actuator management context}/shutdown trace API 요청에 대한 추적 정보를 제공한다. 기본적으로 최근 100건에 대한 정보를 제공한다. true /{actuator management context}/trace
Spring MVC을 사용한다면 아래의 추가적인 endpoint들을 사용할 수 있다.
ID Description Sensitive Default example docs Actuator endpoint들에 대한 샘플 요청 및 응답을 포함한 문서를 제공한다. 브라우져로 해당 주소를 접근하면 해당 문서를 잘 볼 수 있다. 단 spring-boot-actuator-docs 라이브러리를 추가해야 한다. false /{actuator management context}/docs heapdump GZip 방식으로 압축된 hprof heap dump 파일을 제공한다. true /{actuator management context}/heapdump jolokia JMX bean들을 HTTP로 노출하여 준다. 단 jolokia 라이브러리를 추가해야 한다. true /{actuator management context}/jolokia logfile logging.file 혹은 logging.path을 설정 하였다면, 설정된 logfile의 내용을 제공한다. true /{actuator management context}/logfile
Flyway
preparation
dependencies
library
<dependency><groupid>org.flywaydb</groupid><artifactid>flyway-core</artifactid></dependency>
plugin
<plugin><groupid>org.flywaydb</groupid><artifactid>flyway-maven-plugin</artifactid><configuration><url>jdbc:h2:mem:test</url><user>sa</user><password></password></configuration></plugin>
resources
classpath:/db/migration
sql files
create table PERSON ( ID int not null, NAME varchar(100) not null );
Actuator properties
Appendix A. Common application properties의 ACTUATOR PROPERTIES들을 참고하면 된다.
0 notes
yoonyoul-blog · 8 years ago
Text
Work stealing이란?
Work stealing
패러랠 컴퓨팅(parallel computing) 환경에서, working stealing은 다중 쓰레드를 활용한 컴퓨터 프로그램을 위한 스케쥴링(scheduling) 전략이다. working stealing은 동적인 다중 쓰레드 계산 실행상의 문제를 해결하는데, 그중 하나는 고정된 갯수의 프로세서(혹은 코어)를 가진, 정적인 다중 쓰레드 기반의 컴퓨터 상에서 새로운 실행 쓰레드를 생성하는 것이다. Working stealing은 실행 시간, 메모리 사용량 그리고 프로세서간 통신 측면에서 굉장히 효율적이다.
Working stealing 스케쥴러상에서, 컴퓨터 시스템의 개별 프로세서는 계산 작업 그리고 쓰레드와 같은 작업 항목을 위한 큐(queue)를 가지고 있다. 개별 작업 항목은 순차적으로 실행되어야 할 일련의 작업 지시 사항들로 이루어져 있는데, 실제 실행 과정에서 하나의 작업 항목은 동시에 처리 될 수 있는 새로운 작업 항목을 생성할 수도 있다. 이런 새로운 작업 항목들은 생성된 초기엔 해당 작업 항목을 처리할 프로세서의 큐에 포함된다. 특정 프로세서가 자신의 모든 작업 항목 처리를 완료 하였을때, 다른 프로세서의 큐를 살펴보고 처리 되지 않은 작업 항목이 있으면 그 작업 항목을 "훔쳐"와서 처리한다. 사실 work stealing은 모든 프로세서가 유휴 상태에 빠지지 않고 스케쥴링(scheduling)의 과부하가 발생하지 않는 선에서 유휴 상태(놀고 있는) 프로세서에게 작업 계획을 재 분배한다.
Work stealing은 동적 쓰레딩처리를 위한 또 다른 접근 방법인 work sharing과는 대조 되는데, work stealing의 경우 프로세서간 프로세스 이관(migration) 처리량을 줄 일 수 있다. 그 이유는 모든 프로세서가 해야 할 일을 가지고 있다면 프로세서간 프로세스 이관은 발생하지 않기때문이다.
work sharing은 새로운 작업 항목 생성 시, 특정 프로세서에서 실행되도록 스케쥴링 된다.
Work stealing의 아이디어(idea)는 1980년대의 Multilisp 프로그래밍 언어의 구현과 함수형 프로그래밍언어의 병렬처리를 위한 작업으로 거슬러 올라갑니다. Work stealing은 Cilk 프로그래밍 언어를 위한 스케쥴러, 자바(Java)의 fork/join 프레임워크 그리고 .NET의 Task Parallel Libary에 채용되었습니다.
Execution model
Work stealing은 병렬 처리 기법의 "엄격한" fork/join model을 위해 설계 되었습니다. Work stealing을 통해 처리되는 계산은 계산 처리의 시작을 나타내는 단일 소스(source)와 계산의 종료를 나타내는 단일 싱크(sink)를 가지는 비순환 방향 그래프(Directed acyclic graph)로 표현 할 수 있습니다. 그래프에서 각 노드(node)는 fork혹은 join을 나타냅니다. Fork들은 다중의 논리적인 쓰레드 혹은 스트랜드로 불리는 병렬처리를 생산합니다. 노드를 이어주는 선(edge)은 연속적인 계산을 의미합니다.
예를 들어 아래의 Cilk 언어 형식으로 작성된 예제를 보겠습니다.
function f(a, b): c ← fork g(a) d ← h(b) join return c + d function g(a): return a × 2 function h(a): b ← fork g(a) c ← a + 1 join return b + c
f(1, 2) 함수 호출은 아래와 같은 계산 그래프로 나타낼 수 있습니다.
출처: Wikipedia Work_stealing
위의 그래프를 보면, 두개의 선(edge)가 하나의 노드로부터 시작될때, 계산 처리들은 선위의 표기(label)로 표기되며 논리적인 병렬 처리를 말합니다. 그 두개의 선으로 표시된 계산 처리들은 병렬 혹은 순차적으로 처리될 수 있습니다. 계산 처리는 join 노드로 들어오는 선으로 표시되는 계산이 완료되면 해당 join노드에서 진행될 수 도 있습니다. 이제 스케쥴러가 해야 할 일은 전체 계산이 정확한 순서(join 노드들로 제약된)로 실행 될 수 있는 최대한 빨리 완료할 수 있는 방법으로 계산 처리들(edges)을 프로세서에 할당 하는 것입니다.
원본: https://en.wikipedia.org/wiki/Work_stealing
0 notes
yoonyoul-blog · 8 years ago
Text
Spring-boot with Docker 설정
Spring-boot with Docker
Docker 설치하기
home brew로 docker 설치
virtualbox 설치하기
brew cask install virtualbox
Docker, docker-compose, docker-machine 설치
brew install docker docker-compose docker-machine
Docker VM 생성
Docker VM 확인
이미 생성된 Docker VM(Virtual Machine)이 있다면 추가로 설치를 하지 않아도 된다. (해도 상관은 없다) 아래의 명령어를 실행하여 이미 생성된 VM이 있는 지 확인한다.
docker-machine ls
Docker VM 생성
사용 가능한 VM이 없다면 아래의 명령어를 실행하여 VM을 생성한다.
docker-machine create --driver virtualbox [vm name] docker-machine env [vm name] eval "$(docker-machine env [vm name])"
위의 첫번째 라인은 virtualbox 드라이버로 vm name에 해당하는 VM을 생성하는 명령이다. 두번째 라인은 생성된 VM의 환경 변수들을 확인하는 명령이다. 마지막 라인은 docker-cli로 필요한 환경 변수들을 가져오기 위한 명령이다.
docker-maven-plugin추가 및 설정
pom 파일 수정
application project를 docker image로 만들기 위해선 docker-maven-plugin를 추가해야 한다.
<build><plugins> ... <plugin><groupid>com.spotify</groupid><artifactid>docker-maven-plugin</artifactid><configuration><skipdockerbuild>false</skipdockerbuild><imagename>${project.artifactId}</imagename><dockerdirectory>${basedir}/src/main/docker</dockerdirectory><resources><resource><targetpath>/</targetpath><directory>${project.build.directory}</directory><include>${project.build.finalName}.jar</include></resource></resources></configuration></plugin> ... </plugins></build>
Dockerfile 추가
Dockerfile은 Docker container image를 생성하기 위한 base image, 포트 및 파일 추가 등의 지시사항들을 포함하고 있는 파일이다. Spring boot application을 실행하기 위해선 일단 컨테이너(base image)를 만들거나 가져와야 하며 packiging된 application(war 혹은 jar)파일을 컨테이너에 복사해야 하는 일련의 작업을 Dockerfile에 기술해야 한다. 내용은 아래와 같다.
FROM frolvlad/alpine-oraclejdk8:slim VOLUME /tmp EXPOSE 8888 ADD [packiging된 application 파일명] app.jar RUN sh -c 'touch /app.jar' ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-Dspring.profies.active=prod","-jar","/app.jar"]
첫번째 라인은 컨테이너를 맨땅에서 생성하는게 아닌 base image를 가지고 만드는데 이때 사용할 base image를 가져오기 위한 명령이다. 두번째 라인은 임시로 volume을 mount하는 명령이며 세번째 라인은 application에서 사용할 port를 지정한다. 네번째 라인은 packiging된 application을 app.jar라는 이름으로 컨테이너에 추가하는 명령이다. 다섯번째 라인은 현재의 컨테이너 image에서 명령어를 실행하기 위한 것이고 마지막은 컨테이너를 실행가능하도록 설정하기 위한 명령어 이다. 내용은 Javar application을 실행하고 옵션을 전달하는 내용이다. Dockerfile에 추가할 수 있는 지시사항들은 Dockerfile reference를 참조하면 된다.
Dockerfile image 생성
applicaiton project의 root 경로로 이동하여 아래의 명령어를 실행하게 되면 이미지가 생성이 된다. 단 아래의 명령어를 실행하기 전에 docker-cli로 필요한 환경 변수들을 가져와야 한다. 즉 아래의 명령어를 이미지 생성 전에 반드시 실행하는 것이 좋다.
eval "${docker-machine env [vm name]}"
maven wrapper 사용
project내에 mvnw(혹은 mvnw.cmd)파일이 있다면 아래의 명령을 실행하면 된다.
./mvnw clean package docker:build -Dmaven.test.skip=true
maven 사용
mvn clean package docker:build -Dmaven.test.skip=true
Docker 컨테이너 실행
생성된 Docker 컨테이너 이미지를 통해 컨테이너를 실행해야 한다. 아래의 명령어를 통해 Docker 컨테이너를 실행한다.
docker run --rm -p 8888:8888 -e spring.profiles.active=prod --name=[컨테이너 이름] [컨테이너 이미지 이름]
--rm옵션은 이미 컨테이너가 존재하면 삭제하기 위한 옵션이다.
--name 옵션은 Docker 컨테이너의 이름을 지정하기 위한 옵션이다.
-p는 Docker 컨테이너를 실행하는 Host의 port와 Docker 컨테이너의 port를 매핑하여 노출시키기 위한 옵션이다. Docker 컨테이너를 생성할때 8888로 지정하였다. 그리고 Host(예 Local 컴퓨터)의 특정 포트를 해당 컨테이너의 포트(8888)로 연결시켜 주어야 한다.
추가적으로 -e옵션을 주어 컨테이너에서 실행할 application에서 사용할 환경 변수를 설정할 수 있다.
마지막으로 실행할 컨테이너의 이미지를 지정해야 한다. 컨테이너 이미지명은 docker-maven-plugin추가 및 설정의 imageName과 동일하게 지정하면 된다.
Docker 컨테이너 실행 확인
docker-cli를 통해 현재 실행중인 컨테이너들을 확인할 수 있다. 확인 명령은 아래와 같다. 아래의 명령어를 실행하여 현재 컨테이너의 상태를 확인 할 수 있으며 application의 api등을 호출하여 application의 실행 상태를 확인 할 수 있다.
docker ps -a
실행 결과
실행 결과는 아래와 같으며 각 항목의 의미는 쉽게 파악할 수 있다.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cdd0d48aa4d4 config-service "java -Djava.secur..." 3 minutes ago Up 2 minutes 0.0.0.0:8888->8888/tcp config-server
참고
https://github.com/indrabasak/docker-example
https://docs.docker.com
http://chanwookpark.github.io/spring/aws/docker/배포/2016/02/03/springboot-aws-docker/#spring-boot-애플리케이션에-docker-설정하기
0 notes
yoonyoul-blog · 8 years ago
Text
Java Fork/Join
Fork/Join
Fork/join framework은 ExecutorService의 구현체 중 하나로 Fork/join framework를 사용하면 다중 프로세서를 활용할 수 있다. 여러 재귀적으로(쪼개져 나온 작업으로 되돌아 갈 수 있는) 작은 작업으로 쪼개어 처리할 수 있는 작업 처리를 위해 설계 되었다. 애플리케이션 성능을 향상 시킬 수 있도록 모든 가용한 프로세싱 파워를 사용하는 것이 목적이다.
다른 ExecutorService 구현체처럼, fork/join framework은 여러 작업을 쓰레드 풀(pool)내의 워커(worker)쓰레드로 분배한다. Fork/join framework의 차별점은 work-stealing 알고리즘을 사용하여 일을 분배하도록 하는 것이며, 워커 쓰레드가 더이상 처리할 것이 없는 경우, 다른 작업 처리에 바쁜 다른 워커 쓰레드가 처리할 것을 "훔쳐"와서 처리하도록 한다.
Fork/join framework의 핵심은 ForkJoinPool 클래스이며, ForkJoinPool 클래스는 AbstractExecutorService 클래스를 상속/확장한다. ForkJoinPool은 핵심이 되는 work stealing 알고리즘을 구현하고 ForkJoinTask 프로세스들을 실행한다.
기본 사용법
Fork/join framework을 사용하기 위한 처 단계는 하나의 작업을 여러 작업으로 쪼개도록 하는 코드를 작성하는 것이다. 작성해야 할 코드는 아래와 같은 의사코드(pseudocode)와 유사할 것이다.
if (처리해야 할 작업이 충분이 작은가?) 바로 작업을 처리한다. else 처리할 작업을 두개의 작업으로 쪼갠다. 두개의 작업처리를 호출하고 결과를 기다린다. (이부분이 재귀적임)
위의 코드를 ForkJoinTask의 하위 클래스에 구현한다. 보통 보다 특화된 타입인 결과를 반환하는 RecursiveTask 혹은 RecursiveAction을 사용한다.
ForkJoinTask의 하위 구현체가 준비되면 처리할 모든 작업을 나타내는 객체를 생성하고 생성한 객체를 ForkJoinPool인스턴스의 invoke 메서드로 전달한다.
선명도 흐리기 예제
Fork/join framework이 어떻게 작동하는지에 대한 이해도를 높이기 위해, 아래의 예제를 살펴보자. 이미지를 흐기게 처리하길 원한다고 가정하자. 원본 이미지는 정수의 배열로 나타낼 수 있다. 각각의 정수값은 단일 픽셀(pixel)의 색상 값을 나타낸다. 흐림처리된 최종 이미지는 원본 이미지와 동일한 크기의 정소 배열로 나타낼 수 있다.
흐림처리는 원본 배열의 픽셀을 한번에 하나씩 처리해 나가면서 완료될 수 있다. 각각의 픽셀은 주변 픽셀의 색값의 평균값을 취하도록 한다.(빨강, 녹색, 그리고 파랑색의 평균값) 그리고 결과는 최종 배열에 저장한다. 한 이미지는 큰 배열로 표현되기때문에, 이 처리 과정은 오랜 시간이 걸릴 수 있다. 따라서 fork/join framework을 사용하는 알고리짐을 구현한 다중 프로세서 시스템 상의 동시처리의 이점을 활용한다.
``` import java.util.concurrent.RecursiveAction;
public class ForkBlur extends RecursiveAction { private int[] mSource; private int mStart; private int mLengh; private int[] mDestination;
//Processing window size; should be odd. private int mBlurWidth = 15; public ForkBlur(int[] src, int start, int length, int[] dest) { this.mSource = src; this.mStart = start; this.mLengh = length; this.mDestination = dest; } protected void computeDirectly() { int sidePixels = (mBlurWidth -1 ) / 2; for (int index = mStart; index > 16) / mBlurWidth; gt += (float) ((pixel & 0x0ff0000) >> 8) / mBlurWidth; rt += (float) ((pixel & 0x0ff0000) >> 0) / mBlurWidth; } // Reassemble destination pixel. int dpixel = (0xff000000 ) | (((int)rt)
0 notes
yoonyoul-blog · 8 years ago
Text
a-successful-git-branching-model 번역
Git Branching Model
번역 원본 자료: a-successful-git-branching-model/
application을 개발 할때, 소스 관리 및 배포를 위한 branch 관리가 필요하다. 새로운 기능 혹은 결함 수정에 대한 코드들을 어느 branch에 적용해야 하는지, 어떤 흐름을 거쳐 배포를 해야 하는지를 정하고 이를 팀원과 협의하고 준수해야 효과적으로 소스 코드 관리 및 배포를 효율적으로 할 수 있다고 생각된다.
언제 누가 어떤 이유로 코드를 변경했고 언제 배포가 되었는지 추적하기 위한 정책들이 필요하게 되고 아래의 설명한 내용은 배포 전까지의 코드 및 branch를 관리하기 위한 하나의 방안을 기술한다.
(* 출처: http://nvie.com/posts/a-successful-git-branching-model/)
The Main Branches
(* 출처: http://nvie.com/posts/a-successful-git-branching-model/)
master
master branch의 HEAD는 항상 production-ready상태이어야 한다.
develop
develop branch의 HEAD는 항상 다음 릴리즈에 반영될 개발이 완료된 변경 사항들을 포함한 상태이어야 한다. 따라서 통합 branch라고도 불리며 nightly build가 이루어 지는 branch이다.
develop branch내의 코드가 안정된 그리고 release를 할 수 있는 상태가 될때 마다 master branch로 release number를 달고 병합(merge)해야 한다.
Supporting Branches
새로운 기능 혹은 결함을 조치하기 위해서는 새로운 branch가 필요하다. 이런 필요에 의해 만들어지는 branch들은 아래와 같이 feature, release 그리고 hotfix branch들이 있다. 이branch들은 branch를 생성한 목적을 달성한 후에 삭제될 branch로 제한적인 생명주기를 가진다.
Feature branches
(* 출처: http://nvie.com/posts/a-successful-git-branching-model/)
새로운 기능을 개발하기 위한 branch이다. 보통 develop branch를 base로 한다. develop branch를 base로 하기 때문에, 개발 완료 후 다시 develop branch로 병합되어야 한다.
그리고 branch이름은 보통 새로운 기능 번호(예) JIRA Ticket #)를 포함하여야 한다. 다만 master, develop, release-* 그리고 hotfix-*와 같은 단어는 feature branch에 포함하지 않는다.
아래의 예제에서의 feature-#123와 같은 feature branch명을 사용한다.
develop으로부터 feature branch 생성
git checkout -b feature-#123 develop
개발 중 혹은 완료 후, 변경 적용
feature-#123 branch 에 변경 사항을 적용해야 한다.
git add [files] git commit -m "Feature - [ commit message]"
develop branch로 병합
(* 출처: http://nvie.com/posts/a-successful-git-branching-model/)
git checkout develop git merge --no-ff feature-#123
feature branch 삭제
develop branch로 병합 완료 및 모든 테스트 성공 후에 해당 feature branch를 삭제한다.
git branch -d feature-#123
Release branches
새로운 production release를 준비하기 위한 branch이다. 그리고 minor한 결함 수정 이나 version number, build 날짜 등등을 포함하는 meta-data를 준비를 포함한다. feature branch와 비슷하게 보통 develop branch를 base로 한다. 그렇지만 병합은 develop 그리고 master로 수행한다.
branch명은 release-*의 명명규칙을 사용하는 것이 좋다.
release branch를 만들 시점에는 모든 변경 사항이 develop branch에 반영이 되어 있어야 하며 반드시 테스트를 거치고 모든 기능이 정상 동작하는지가 확인되어야 한다.
만약 개발 혹은 결함이 발견된 새로운 기능 변경 사항이 있다면, 다음 release branch를 통해 배포하는 것이 좋다. 즉, release branch가 생성된 후에, develop branch로 병합하는 것이 좋다.
release branch를 생성 할때, version number를 결정하고 release notes를 작성하는 것이 좋다.
develop으로부터 release branch 생성
git checkout -b release-[version number or release numbrer] develop
master branch로 병합
release를 하기위한 모든 작업이 완료되면 release branch를 master branch로 병합하고 master branch를 tagging한다.
git checkout master git merge --no-ff release-[version number or release numbrer] git tag -a [version number or release numbrer] git tag -l
develop branch로 병합
master로의 병합 후에, release branch의 변경 사항을 develop branch에 적용한다. 이때 보통 code conflict가 발생하게 된다. 발생한 code conflict는 모두 수정하여 커밋해야 한다.
git checkout develop git merge --no-ff release-[version number or release numbrer]
release branch 삭제
모든 release작업이 완료 된 후에 해당 release branch가 필요 없게 되면 release branch를 삭제한다.
git branch -d release-[version number or release numbrer]
Hotfix branches
(* 출처: http://nvie.com/posts/a-successful-git-branching-model/)
Hotfix branch는 production에 반영할 새로운 release를 준비하기 위한 branch라는 점에서 release branch와 매우 유사하다. 다만 hotfix이기때문에 사전에 계획되지 않았다는 것이 다르다.
production에서 심각한 버그가 발견되었을때, 이를 수정하기 위한 긴급 패치를 작성하기 위한 branch로 버그가 발견된 version(혹은 release) number로 tag된 master branch를 base로 생성한다.
master로부터 hotfix branch 생성
hotfix branch를 생성할때 minor 버전 번호를 증가 시켜야 한다.
git checkout -b hotfix-[version number or release numbrer] master
특정 tag로부터 hotfix branch 생성
git checkout -b hotfix-[version number or release numbrer] [TAG number]
master branch로 병합
hotfix를 release 하기위한 모든 작업이 완료되면 hotfix branch를 master branch로 병합하고 master branch를 tagging한다.
git checkout master git merge --no-ff hotfix-[version number or release numbrer] git tag -a [version number or release numbrer] git tag -l
develop branch로 병합
git checkout develop git merge --no-ff hotfix-[version number or release numbrer]
release branch로 병합
만약 hotfix를 작성하고 완료할때, release branch가 존재한다면 release branch로도 hotfix의 내용을 병합해줘야 한다. 이때 release branch는 최종적으로 develop branch로 병합이 될 것이기때문에 develop branch대신 release branch에 병합을 한다.
참고 사항
cherry-pick
특정 branch에 반영된 커밋 중 하나를 골라 다른 branch에 적용하는 것을 말한다. 해당 commit을 반영할 branch로 먼저 이동 후에 반영할 commit-hash를 cherry-pick명령을 통해 branch에 반영한다.
git checkout [commit을 반영할 branch] git cherry-pick [commit-hash]
cherry-pick은 기존 history를 변경하는 것이 아니라 반영할 commit을 해당 branch의 history에 추가를 하는 것이다.
fast-forward
더이상 병합 작업이 필요 없는 상황에서 특정 branch의 HEAD pointer를 갱신하는 것을 의미한다. 보통의 merge 작업에선 새로운 commit을 발생 시키지만 fast-forward는 commit없이 HEAD의 pointer를 가장 마지막 커밋을 참조하도록 갱신한다. 아래의 명령어를 통해 HEAD의 현재 pointer를 알 수 있다.
git show-ref --head
병합을 할때, fast-forward가 발생할 수 있는 상황이라도 반드시 새로운 commit을 만들고 싶다면 --no-ff (no fast forward) 옵션을 주고 병합을 하면 된다.
git merge --no-ff feature-branch
0 notes