-- 계정 생성

> create user ${User}@${Host} identified by ${Password}

-- User = login id
-- Host = remote ip | '%' (all)
-- Password = 486 (윤하...)
(하루에 네번 사랑을 말하고...여덟번 웃고...여섯번의...)


-- 모든 권한 부여
> grant all privileges on ${DB}.${Table} TO ${User}@${Host} 
  -- option identified by ${Password};
  
-- option 적용시 비밀번호 바뀜
-- DB = 적용할 Database ( possible * )
-- Table = 적용할 Table ( possible * )

-- 권한 적용
> flush privileges

-- 권한 확인
> show grants for ${User}@${Host}

본래 Bitbucket의 Id/Pw를 사용해서 연동하려 했으나,

지속적으로 returned status code 128: 를 뱉음.

 

원인을 알 수 없는 와중에 구글링 해보니,

repository 생성시 private으로 생성한 경우 id/pw를 사용한 연결이 불가능 하다고...ㅠ

 

그래서 sshkeygen을 사용해서 쌍키 생성 후 

공개키는 bitbucket에 비밀키는 젠킨스 계정관리에 등록해줌

 

그리고 Git 접속 경로는 https가 아닌 ssh 접속

회사에서 필요한 기능중 주소를 기반으로 우편번호를 매칭해서 넣는 기능이 필요.

했었던 적이 있었어요.

 

그때는 여차저차 하면서 정리를 안해놔서...

한 8개월 정도 지난상태인데, 주소가 많이 업데이트 되었을테니,

다시 업데이트 할 필요가 있다고 생각!!

 

하는김에 정리를 해보자.

 

https://www.juso.go.kr/addrlink/addressBuildDevNew.do?menu=rdnm

 

건물DB | 도로명주소 DB 다운로드 | 도로명주소 개발자센터

건물DB (구 전체주소) --> 도로명주소를 구성하는 기본 단위인 건물정보와 해당 건물이 위치한 토지(지번)정보로 구성된 DB입니다. 하나의 도로명주소가 부여된 단지형 아파트의 경우에도 상세 동

www.juso.go.kr

 

일단 위 사이트에서 건물DB 전체자료를 받자.

워낙 안했으니 전체자료를 하는게 속편할듯.

 

그리고 합쳐줘야 하기도 하지만...변환도 해줘야함.

해당 파일은 euc-kr이라 iconv 를 이용해 utf-8로 해줘야함~!

 

> cat *.txt > address.txt
> ll
-rw-r--r-- 1 root root 1657160613  6월 18 10:40 address.txt
-rw-r--r-- 1 root root   64284639  6월 18 10:30 build_busan.txt
-rw-r--r-- 1 root root   95161072  6월 18 10:30 build_chungbuk.txt
-rw-r--r-- 1 root root  134380135  6월 18 10:32 build_chungnam.txt
-rw-r--r-- 1 root root   41286526  6월 18 10:30 build_daegu.txt
-rw-r--r-- 1 root root   22491386  6월 18 10:31 build_daejeon.txt
-rw-r--r-- 1 root root   88129338  6월 18 10:32 build_gangwon.txt
-rw-r--r-- 1 root root   26507714  6월 18 10:32 build_gwangju.txt
-rw-r--r-- 1 root root  219161506  6월 18 10:34 build_gyeongbuk.txt
-rw-r--r-- 1 root root  199317967  6월 18 10:34 build_gyeongnam.txt
-rw-r--r-- 1 root root  252300939  6월 18 10:37 build_gyunggi.txt
-rw-r--r-- 1 root root   41208599  6월 18 10:35 build_incheon.txt
-rw-r--r-- 1 root root   40241010  6월 18 10:35 build_jeju.txt
-rw-r--r-- 1 root root  124816202  6월 18 10:37 build_jeonbuk.txt
-rw-r--r-- 1 root root  175555503  6월 18 10:39 build_jeonnam.txt
-rw-r--r-- 1 root root    8595016  6월 18 10:37 build_sejong.txt
-rw-r--r-- 1 root root   98541954  6월 18 10:38 build_seoul.txt
-rw-r--r-- 1 root root   25181107  6월 18 10:39 build_ulsan.txt

> iconv -c -f euc-kr -t utf-8 address.txt -o utf8_address.txt

 

이번에 하면서 알게된건데, 합치고 변환하지 말고 그냥...

> iconv -c -f euc-kr -t utf-8 *.txt -o address.txt 

이렇게 하면 합치기도하고 변환도 하고...한방에 되는...

두줄 입력할거 한줄하는거라 큰 차이는 없지만 에헴 ㅎㅎ

 

 

요새 구주소(지번주소) 사용하는분들중 인터넷쇼핑 하는분들 별로 안계실테니...도로명주소만 하는걸로 ㅎㅎ

 

그리고 vi로 열어서 헤더를 입력해줘야함!

lawTownCode|city|ward|town|lee|isMountaion|jibun|jibunSub|roadCode|road|isUnderground|buildingNumber|buildingNumberSub|buildingName|buildingNameDetail|buildingControlNumber|dongNumber|dongCode|dongName|zipcode|zipcodeNumber|massDlvr|moveReasonCode|stdnDate|preRoadName|cityBuildingName|isPublic|newZipCode|idDetailAddr|etc1|etc2

 

이제 logstach conf 파일을 만들어야함.

> vi logstash_address.conf

input {
  file {
    path => "/경로/address/202005/address.txt"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}
filter {
  csv {
      separator => "|"
      columns => ["lawTownCode","city","ward","town","lee","isMountaion","jibun","jibunSub","roadCode","road","isUnderground","buildingNumber","buildingNumberSub","buildingName","buildingNameDetail","buildingControlNumber","dongNumber","dongCode","dongName","zipcode","zipcodeNumber","massDlvr","moveReasonCode","stdnDate","preRoadName","cityBuildingName","isPublic","newZipCode","idDetailAddr","etc1","etc2"]
      add_field => {"createDateTime" => "2020-05-01 12:12:12"}
      remove_field => ["@version", "host", "@timestamp"]

  }
}
output {
    elasticsearch {
        document_id => "%{buildingControlNumber}"
        hosts => "localhost"
        index => "zipcode"
    }
}

es 홈피 찾아보면 되는 간단한 문법들!

그래도 잘 모르겠다면

아래 허민석님의 채널 참고!

https://www.youtube.com/watch?v=iq3t7X_9URA&list=PLVNY1HnUlO24LCsgOxR_eK2Yi4sOgH9Pg&index=18

 

저는 로그스태시 사용하는걸 위에서 배웠어요~!

좋은건 나눠야지 ㅎㅎ

 

 

이제 실행~

/usr/share/logstash/bin/logstash -f logstash_address.conf 

한참 걸려요...

 

이후 키바나 매트릭스 date range 걸어서 확인..얼마나 넘어왔는ㅈㅣ...ggg

작년에 넣은 기존 데이터가 천만건 정도니까..얘도 그것보다 많으면 많지 적지는 않을테니...

매번 하는 작업인데 항상 중간부분을 까먹어서 헤매다 보니, 정리를 좀 해두는게 좋겠다 싶어서..

 

톰캣은 일단 tar.gz 파일을 받고,

 

폴더 구조는 app 과 web 으로 구분을 짓는다.

 

app 아래에 여러 톰캣을 넣어놓고, web아래는 webapps를 옮겨서 해당 서비스 이름으로 변경한다.

 

톰캣/conf/server.xml 의 docBase를 확인해보면 webapps로 되어 있을텐데,

이걸 나는 web 아래로 복사해서 서비스명으로 변경하였기 때문에,

 

docBase="/web/서비스명/"으로 해준다.

 

이렇게 구조를 나누는 이유는...전에 SE 한분을 일한적이 있는데 이렇게 하는게 깔끔하다고 해서...ㅎㅎ

 

그리고 ci에서 배포를 하기 위해 tomcat-users.xml 또한 설정 한다.

 

'

설정이 제대로 안되었을 경우 매니저 접속시 이런 페이지를 만날 수 있다.

 

manager-* 각 롤을 마다 역할이 다르며, ci에서 이용하기 위한 롤은 script다.

 

여기까지는 늘상 알고 있고 딱히 헷갈릴것도 없는데...

 

항상 이부분이 문제

딱히 문제랄건 없고, 꼭 이 페이지를 만나야 기억이 난다 ㅠㅠ

 

manager/META-INF/context.xml 을 확인해보면

<Context> 태그 아래에 기본적으로 두개의 밸브가 있다.

이 설정은 외부에서 톰캣 매니저로의 접속을 막는 설정이며,

해당 설정을 주석처리 해주거나, ip를 정확히 지정해주면 외부에서 매니저로 접속이 가능하다.

 

운영환경에서 보통 톰캣과 ci를 같은서버에 두지 않기에, 이정도 설정을 알아놓으면 아주 유용할듯.

 

그럼 오늘도 고생하세유.

회사에서 공교롭게도 우분투를 14와 16을 사용...

근데 두 버전간 차이가 생각보다 다양하네요.

 

이러한 저런한 문제들도 많았지만,

가장 중요한 서비스 등록 방법도 다르기도하고!

 

일단 구글링을 통해 정리를 해보겠어요.

 

 

 

14.04 LTS

 

1. /etc/init.d 에 서비스 파일 생성

2. 파일내용은 뭐 마다 다르겠지만 대강 이런...

### BEGIN INIT INFO
# Provides: tomcat
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Apache Tomcat
### END INIT INFO

#source /etc/profile

tomcat=${tomcat_path} #톰캣 설치 경로
startup=$tomcat/bin/startup.sh
shutdown=$tomcat/bin/shutdown.sh

start() {
 echo -n $"Starting Tomcat service: "
 sh $startup
 echo $?
}

stop() {
 echo -n $"Stopping Tomcat service: "
 sh $shutdown
 echo $?
}

restart() {
 stop
 start
}

status() {
 ps -aef | grep nhccFront | grep -v .out | grep -v /bin/sh | grep -v grep
}

# Handle the different input options
case "$1" in
start)
 start
 ;;
stop)
 stop
 ;;
status)
 status
 ;;
restart)
 ;;
*)
 echo $"Usage: $0 {start|stop|restart|status}"
 exit 1
esac

exit 0

3. update-rc.d 서비스파일명 defaults

 

 

 

16.04 LTS

1. /etc/systemd/system 에 서비스 파일 생성

2. 파일내용은 14버전과 완전 다름...

[Unit]
Description=Apache Tomcat Web Application Container After=network.target

[Service]
Type=forking
ExecStart=$tomcat/bin/startup.sh
ExecStop=$tomcat/bin/shutdown.sh
RestartSec=10
Restart=always

[Install]
WantedBy=multi-user.target

3. systemctl enable 서비스파일명

 

뭔가 꼬여서 잘 안된다면...

systemctl daemon-reload

systemctl reset-failed

위 두줄을 이용 하면 끝

 

또한 등록된 서비스를 확인 할 경우

 

systemctl list-units --type service

위 명령어를 이용하지만 서비스가 표시되지 않을 경우에는

systemctl list-units --type service --all을 하면 나와유

 

 

 

'서버 > 리눅스' 카테고리의 다른 글

특정 내용을 포함한 파일 찾기  (0) 2022.02.21
Ubuntu 계정 생성 및 sudo 권한 부여  (0) 2019.04.05

지금 당장 플러그인 개발이 시급하다면, 

아래 링크와 하단쪽 내용만 확인하세요!

 

(왠지 나 처럼 엄청 헤매고 있는 사람 많을듯...)

 

짬이나서 초성 검색을 위해 필터를 만들고자 마음을 먹고

다시 아래 글을 보았어요.

http://blog.naver.com/tmondev/220918935030

 

ES플러그인 커스터마이징 (3)ㅎㅇㅇㅎ검색 대응하기

티몬의 자동완성ES플러그인 커스터마이징을 다루는 마지막 3부의 주제로 한글 검색에 필수적인 자모 분해...

blog.naver.com

 

소스코드는 그대로 사용해도 무방하다는 댓글을 확인 후..

"어 그냥 만들면 되겠지?" 라고 생각을 하고 열심히 Ctrl + c & Ctrl + v를 하였습니다.

 

근데 플러그인을 적용하는 부분의 내용은 있지만, zip파일은 어떻게 생성할 것이며,

기타 잡다한 것들은 어찌하는지 하나도 모르겠어서 ㅠㅠ

 

게다가 "AbstractTokenFilterFactory를 상속받은 클래스는 어떻게 호출하는거징? " 오징 거징 ㅠㅠ

 

https://m.blog.naver.com/tmondev/220916240095

 

ES플러그인 커스터마이징 (2)핫딜 검색순위 조절하기

들어가며티몬에서는 고객에게 검색 서비스를 제공하기 위해 검색엔진으로 엘라스틱 서치(Elasticsearch, ...

blog.naver.com

이 글을 보면 AbstractPlugin을 상속 받아서 해당 내용에서 처리하는걸 확인 할 수 있는데요.

 

문제는 2버전인가...부터 AbstractPlugin가 없어졌다는 사실..

 

그러다가 외국어로된 블로그(영어지만 코드는 이해할수있으니 ㅋㅋㅋ) 막 찾다보니 AbstractModule?을 사용하는 것도 있었어요.

 

http://jfarrell.github.io/

 

http://jfarrell.github.io/

Creating an Elasticsearch Plugin Elasticsearch is a great search engine built on top of Apache Lucene. We came across the need to add new functionality and did not want to fork Elasticsearch for this. Luckily Elasticsearch comes with a plugin framework. We

jfarrell.github.io

 

일단 첫번째 문제는...AbstractModule을 사용해서 해결해 보도록 ...해보자 였습니다.

public class ChosungPlugin extends AbstractModule {

	private final Settings settings;
	
	public ChosungPlugin(Settings settings) {
		this.settings = settings;
	}
	
	@Override
	protected void configure() {
		bind(JamoTokenFilterFactory.class).asEagerSingleton();
	}

	
}

 

여기서 당면한 두번째 문제...

"압축파일은 어찌 만들지?"

 

저는 플러그인 사용해본건 은전한닢과 아리랑? 같은 한글사전 관련된 플러그인 뿐이었죠.

(이제는 nori를 쓰지만..헤헤)

 

근데 아시다시피 전부 zip파일 이잖아요?

jar 파일은 어찌어찌 만들겠는데...흠...

뭐 저는 잘 모르지만 위에 링크 남긴 글에 해당 내용이 포함 되어 있어요!

 

그래서 그 내용을 바탕으로 만들어서 mvn clean install을 했죠.

 

뭐 약간의 오타를 (복붙했는데 왠 오타, 사실 타이핑이 하고싶어서...ㅠㅠ) 수정하니 잘 돌아갔어요!

 

새로운것도 알게 되었죠!

 

메이븐의 플러그인중 maven-assembly-plugin를 이용하고,

<assembly> 의 태그를 남기면 해당 내용을 수행한다는것 정도?

이걸 통해 zip으로 압축을 한다는것 정도??

 

여튼 이렇게 압축을 하고 해당 플러그인을 es에 심었어요.

 

elasticsearch-plugin.bat install file:///elastic\elasticsearch-7.1.1/elasticsearch-chosungPlugin-7.1.1.zip

 

아니 근데 자꾸 이런 결과가 나오는 거에요 ㅠㅠ

-> Downloading file:///elastic\elasticsearch-7.1.1/elasticsearch-chosungPlugin-7.1.1.zip
[=================================================] 100%??
Exception in thread "main" java.nio.file.NoSuchFileException: C:\elastic\elasticsearch-7.1.1\plugins\.installing-9326766450384708165\plugin-descriptor.properties
        at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
        at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
        at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
        at java.base/sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:235)
        at java.base/java.nio.file.Files.newByteChannel(Files.java:373)
        at java.base/java.nio.file.Files.newByteChannel(Files.java:424)
        at java.base/java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
        at java.base/java.nio.file.Files.newInputStream(Files.java:158)
        at org.elasticsearch.plugins.PluginInfo.readFromProperties(PluginInfo.java:156)
        at org.elasticsearch.plugins.InstallPluginCommand.loadPluginInfo(InstallPluginCommand.java:714)
        at org.elasticsearch.plugins.InstallPluginCommand.installPlugin(InstallPluginCommand.java:793)
        at org.elasticsearch.plugins.InstallPluginCommand.install(InstallPluginCommand.java:776)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:231)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:216)
        at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.MultiCommand.execute(MultiCommand.java:77)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.Command.main(Command.java:90)
        at org.elasticsearch.plugins.PluginCli.main(PluginCli.java:47)

 

plugin-descriptor.properties 가 필요한가봐요...

 

아 이건 뭐지 하고 한참을 찾아보니...

위 글 내용의 ES 버전을 보니 0.17.1...뚜둥!!

 

정확히 몇 버전 부터 인지는 모르나, plugin 설치시 해당 내용에는 plugin-descriptor.properties 이게 있어야 하더라고요!

 

그래서 저는 일단 대~충

http://www.technocratsid.com/how-to-create-an-elasticsearch-6-4-1-plugin/

 

How to create an Elasticsearch 6.4.1 Plugin

A plugin provides a way to extend or enhance the basic functionality of Elasticsearch without having to fork it from GitHub. Elasticsearch supports a plugin framework which provides many custom plu…

www.technocratsid.com

위 글을 참고 했어요~!!

이것 말고도 좀 이것저것 참고를 했는데...

여튼 만들고 적용하는데~~

 

같은 오류가 또 나는거에요~?

아 뭐지 하고 한참을 헤맸는데 역시나 오타...(아 복붙할걸...)

파일 이름을 plugin-descriptor.properties로 했어야 했는데 plugin-descrptor.properties로 했더라...는 전설이

(또한 zip압축시 루트에 존재하지 않고 폴더를 하나 지니고 있을 경우 동일한 에러발생)

여튼 이 문제는 지나고 나니!

 

C:\elastic\elasticsearch-7.1.1\bin>elasticsearch-plugin.bat install file:///elastic\elasticsearch-7.1.1/chosung-plugin-1.0.zip
-> Downloading file:///elastic\elasticsearch-7.1.1/chosung-plugin-1.0.zip
[=================================================] 100%??
Exception in thread "main" java.lang.IllegalArgumentException: property [classname] is missing for plugin [chosung-plugin]
        at org.elasticsearch.plugins.PluginInfo.readFromProperties(PluginInfo.java:192)
        at org.elasticsearch.plugins.InstallPluginCommand.loadPluginInfo(InstallPluginCommand.java:714)
        at org.elasticsearch.plugins.InstallPluginCommand.installPlugin(InstallPluginCommand.java:793)
        at org.elasticsearch.plugins.InstallPluginCommand.install(InstallPluginCommand.java:776)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:231)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:216)
        at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.MultiCommand.execute(MultiCommand.java:77)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.Command.main(Command.java:90)
        at org.elasticsearch.plugins.PluginCli.main(PluginCli.java:47)

C:\elastic\elasticsearch-7.1.1\bin>elasticsearch-plugin.bat install file:///elastic\elasticsearch-7.1.1/chosung-plugin-1.0.zip
-> Downloading file:///elastic\elasticsearch-7.1.1/chosung-plugin-1.0.zip
[=================================================] 100%??
Exception in thread "main" java.lang.IllegalArgumentException: Unknown properties in plugin descriptor: [plugin]
        at org.elasticsearch.plugins.PluginInfo.readFromProperties(PluginInfo.java:233)
        at org.elasticsearch.plugins.InstallPluginCommand.loadPluginInfo(InstallPluginCommand.java:714)
        at org.elasticsearch.plugins.InstallPluginCommand.installPlugin(InstallPluginCommand.java:793)
        at org.elasticsearch.plugins.InstallPluginCommand.install(InstallPluginCommand.java:776)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:231)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:216)
        at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.MultiCommand.execute(MultiCommand.java:77)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.Command.main(Command.java:90)
        at org.elasticsearch.plugins.PluginCli.main(PluginCli.java:47)

이번에는 해당 프로퍼티 안에 정의된 내용이 문제가 있는 친절한 설명입니다.

1. 플러그인으로 사용될 classname을 지정해야 함.

2. plugin 이라는 프로퍼티는 모르는놈이다...(모르는놈이면 그냥 안쓰면 그만 아닌가...)

 

여튼 두가지 친절한 로그로 인해 수정 후 다시 시도~

C:\elastic\elasticsearch-7.1.1\bin>elasticsearch-plugin.bat install file:///elastic\elasticsearch-7.1.1/chosung-plugin-1.0.zip
-> Downloading file:///elastic\elasticsearch-7.1.1/chosung-plugin-1.0.zip
[=================================================] 100%??
Exception in thread "main" java.lang.IllegalStateException: failed to load plugin chosung-plugin due to jar hell
        at org.elasticsearch.plugins.PluginsService.checkBundleJarHell(PluginsService.java:524)
        at org.elasticsearch.plugins.InstallPluginCommand.jarHellCheck(InstallPluginCommand.java:765)
        at org.elasticsearch.plugins.InstallPluginCommand.loadPluginInfo(InstallPluginCommand.java:728)
        at org.elasticsearch.plugins.InstallPluginCommand.installPlugin(InstallPluginCommand.java:793)
        at org.elasticsearch.plugins.InstallPluginCommand.install(InstallPluginCommand.java:776)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:231)
        at org.elasticsearch.plugins.InstallPluginCommand.execute(InstallPluginCommand.java:216)
        at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.MultiCommand.execute(MultiCommand.java:77)
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124)
        at org.elasticsearch.cli.Command.main(Command.java:90)
        at org.elasticsearch.plugins.PluginCli.main(PluginCli.java:47)
Caused by: java.lang.IllegalStateException: jar hell!

"어휴 이건 또 뭐지 jar hell? jar 지옥?ㅋㅋㅋ"

 

이 부분은 여러글을 찾아보니...대강 짐작이 가서 ...

 

위에 빌드시 depency 한 luncene jar가 두개 존재하는데...

이게 이미 es에서 존재하고 사용중인거라 필요가 없다는 내용인듯했다.

 

근데 나는 zip 을 만들때 include로 chosung-plugin-1.0.jar만 지정 했는데도, 

이게 들어와 있으니, 영문을 모를 따름... ㅎㅎ

 

그냥 압축 해제 후 두개의 jar를 포함하지 않은 상태로 다시 zip으로 압축했네요.

(반디집으로 ㅋㅋ)

 

여튼 이후 플러그인 설치는 잘 된듯 하고!

ES를 구동하니...

 

[2019-08-01T16:46:48,649][INFO ][o.e.e.NodeEnvironment    ] [KYN-PC] using [1] data paths, mounts [[(C:)]], net usable_space [91.1gb], net total_space [237.8gb], types [NTFS]
[2019-08-01T16:46:48,668][INFO ][o.e.e.NodeEnvironment    ] [KYN-PC] heap size [989.8mb], compressed ordinary object pointers [true]
[2019-08-01T16:46:48,676][INFO ][o.e.n.Node               ] [KYN-PC] node name [KYN-PC], node ID [w79MYvdUQguvtunBL1Vrgw], cluster name [elasticsearch]
[2019-08-01T16:46:48,680][INFO ][o.e.n.Node               ] [KYN-PC] version[7.1.1], pid[7744], build[default/zip/7a013de/2019-05-23T14:04:00.380842Z], OS[Windows 10/10.0/amd64], JVM[Oracle Corporation/OpenJDK 64-Bit Server VM/12.0.1/12.0.1+12]
[2019-08-01T16:46:48,685][INFO ][o.e.n.Node               ] [KYN-PC] JVM home [C:\elastic\elasticsearch-7.1.1\jdk]
[2019-08-01T16:46:48,687][INFO ][o.e.n.Node               ] [KYN-PC] JVM arguments [-Xms1g, -Xmx1g, -XX:+UseConcMarkSweepGC, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -Des.networkaddress.cache.ttl=60, -Des.networkaddress.cache.negative.ttl=10, -XX:+AlwaysPreTouch, -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -XX:-OmitStackTraceInFastThrow, -Dio.netty.noUnsafe=true, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCapacityPerThread=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disable.jmx=true, -Djava.io.tmpdir=C:\Users\comyn\AppData\Local\Temp\elasticsearch, -XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=data, -XX:ErrorFile=logs/hs_err_pid%p.log, -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m, -Djava.locale.providers=COMPAT, -Dio.netty.allocator.type=unpooled, -Delasticsearch, -Des.path.home=C:\elastic\elasticsearch-7.1.1, -Des.path.conf=C:\elastic\elasticsearch-7.1.1\config, -Des.distribution.flavor=default, -Des.distribution.type=zip, -Des.bundled_jd=true]
[2019-08-01T16:46:51,086][WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [KYN-PC] uncaught exception in thread [main]
org.elasticsearch.bootstrap.StartupException: java.lang.ClassCastException: class com.mmv.chosung.ChosungPlugin
        at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:163) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:150) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) ~[elasticsearch-cli-7.1.1.jar:7.1.1]
        at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:115) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92) ~[elasticsearch-7.1.1.jar:7.1.1]
Caused by: java.lang.ClassCastException: class com.mmv.chosung.ChosungPlugin
        at java.lang.Class.asSubclass(Class.java:3646) ~[?:?]
        at org.elasticsearch.plugins.PluginsService.loadPluginClass(PluginsService.java:581) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.plugins.PluginsService.loadBundle(PluginsService.java:555) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.plugins.PluginsService.loadBundles(PluginsService.java:471) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.plugins.PluginsService.<init>(PluginsService.java:163) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.node.Node.<init>(Node.java:308) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.node.Node.<init>(Node.java:252) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Bootstrap$5.<init>(Bootstrap.java:211) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:211) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:325) ~[elasticsearch-7.1.1.jar:7.1.1]
        at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:159) ~[elasticsearch-7.1.1.jar:7.1.1]
        ... 6 more

어 classcastexception?? 

아 위에서 간과한 문제란 비슷한거 같은데...

plugin 클래스 만들때 AbstractModule를 상속받은게 원인이 아닌가 싶더군요.

AbstractModule는 상당히 과거부터 존재했던 클래스인데...용도가 어디인지 모르겠지만...

다른걸 찾아보기로 하고...

 

두번째 영어 포스팅을 보면 extends Plugin을 사용한걸 확인 할 수 있어요.

 

문제는 이건 목적에 맞는 인터페이스를 상속받어야 한다는건데...

 

얘네들 전부가 관련 플러그인...음 이중에 누가 봐도 AnalysisPlugin...이겠지

 

문제는 또 이걸 어찌 쓰나 찾어봐야해서..

해당 부분은 너무 친절하게 잘 설명 되어있다..ㅎㅎ

AnalysisPlugin.java 상단 코멘트

getCharFilters, getTokenFilters, getTokenizers 등 다양하게 있는데,

나는 토큰필터 니까...그냥 저대로 쓰면 될듯..

그래서 나도 똑같이...

public class ChosungPlugin extends Plugin implements AnalysisPlugin{
	
	@Override
	public Map<String, AnalysisProvider<TokenFilterFactory>> getTokenFilters() {
        return singletonMap("chosung_filter", JamoTokenFilterFactory::new);
    }
}

이렇게 하면 ES안에서 필터이름은 chosung_filter로 사용하면 된겠지비...

 

어휴 이제 다시 한번 zip파일을 만들고...

플러그인 설치하니... jar hell 다시 오류나고...압축해제 후 루씬 관련 jar 제외한 상태로 다시 압축~~

설치 후 ES 가동!

음 아주 잘 되는구만!

 

근데 이 필터가 정말 잘 작동할까 의심을 하며, 티몬 김광문님의 블로그에 있는 스크립트를 돌려 보았다!

 

ES 버전차이가 있어서 아주 약간 수정만 하면 되는데 여튼 잘 된다~!!

이제 이걸 운영서버에 반영만 하면 될듯!!

 

도움되는 글을 남겨주신 티몬의 김광문님 포함 외국인 형아들에게 감사를 표하며, 그럼 안녕.

 

 

추가적으로 이 글을 보고도 헤맬 사람들이 걱정되어 코드 몇줄 더 남겨요...

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mmv</groupId>
	<artifactId>chosung-plugin</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>
	<name>chosungPlugin</name>

	<url>http://maven.apache.org</url>

    <properties>
        <elasticsearch.version>7.1.1</elasticsearch.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
        <elasticsearch.assembly.descriptor>${basedir}/src/main/assemblies/plugin.xml</elasticsearch.assembly.descriptor>
    </properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>${maven.compiler.plugin.version}</version>
				<configuration>
					<source>${maven.compiler.target}</source>
					<target>${maven.compiler.target}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<appendAssemblyId>false</appendAssemblyId>
					<outputDirectory>${project.build.directory}/releases/</outputDirectory>
					<descriptors>
						<descriptor>${elasticsearch.assembly.descriptor}</descriptor>
					</descriptors>
				</configuration>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>attached</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
		<resources>
			<resource>
				<directory>src/main/resources</directory>
				<filtering>true</filtering>
				<includes>
					<include>**/*.properties</include>
				</includes>
			</resource>
		</resources>
	</build>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.elasticsearch</groupId>
			<artifactId>elasticsearch</artifactId>
			<version>${elasticsearch.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-analyzers-common</artifactId>
			<version>8.0.0</version>
		</dependency>
	</dependencies>
</project>

pom.xml

 

<?xml version="1.0"?>
<assembly>
	<id>plugin</id>
	<formats>
		<format>zip</format>
	</formats>
	<includeBaseDirectory>false</includeBaseDirectory>
	<fileSets>
		<fileSet>
			<directory>target</directory>
			<outputDirectory>/</outputDirectory>
			<includes>
				<include>*.jar</include>
			</includes>
		</fileSet>
	</fileSets>
	<files>
		<file>
			<source>${project.basedir}/src/main/resources/plugin-descriptor.properties
			</source>
			<outputDirectory>/</outputDirectory>
			<filtered>true</filtered>
		</file>
		<file>
			<source>${project.basedir}/src/main/resources/plugin-security.policy
			</source>
			<outputDirectory>/</outputDirectory>
			<filtered>false</filtered>
		</file>
	</files>
	<dependencySets>
		<dependencySet>
			<outputDirectory>/</outputDirectory>
			<unpack>false</unpack>
		</dependencySet>
	</dependencySets>
</assembly>

plugin.xml

 

description=${project.description}
version=${project.version}
name=${project.artifactId}
classname=com.mmv.chosung.ChosungPlugin
java.version=1.8
elasticsearch.version=7.1.1

plugin-descriptor.properties

 

grant{
permission java.security.AllPermission;
};

plugin-security.policy (이건 뭐 참고용으로 안에 저런 코드가 있는거라던데...일단은 나도 씀)

 

이 외의 코드는 어차피 참조한 링크들 안에 전부 있는것들이기에 생략하기로 할게용~

es에 데이터도 저장하고~ 간단한 검색도 하고~

spring에서 사용해야 하는데...

어찌 좀 편하게 방법이 없을까... 고민하던중...

 

spring.io 에 보면 어지간한 저장소와 연결할 수 있는 spring-data 프로젝트가 있더군요!

 

음...그래서 이것저것 막 삽질해보는데...뭐가 버전이 안맞고 안되고..어휴 ㅠㅠ

maven에서 jar 참조하는 버전을 변경하고 해보는데도..안되고... 어휴 ㅠㅠ

 

es에 비해 버전업이 너무 느려 ㅠㅠ

es 버전을 7.1.1을 사용하는데...

 

결국 대강 JPA 흉내라도 내보자는 심산으로...만들어 봐야겠다~~

 

결심

 

일단 PO인 도메인(이하 Docu)부터 생성...


@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@lombok.ToString
@Document(indexName="zipcode")
public class DocuZipcode implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -3670872846932659171L;

	@Id
	public String buildingControlNumber;
	
	public String lawTownCode;
	
	public String city;
	
	public String ward;
	
	public String town;
	
	public String lee;
	
	public String isMountaion;
	
	public String jibun;
	
	public String jibunSub;
	
	public String roadCode;
	
	public String road;
	
	public String isUnderground;
	
	public String buildingNumber;
	
	public String subBuildingNumber;
	
	public String buildingName;
	
	public String buildingNameDetail;
	
	public String dongNumber;
	
	public String dongCode;
	
	public String dongName;
	
	public String zipcode;
	
	public String zipcodeNumber;
	
	public String massDlvr;
	
	public String moveReasonCode;
	
	public String stdnDate;
	
	public String preRoadName;
	
	public String cityBuildingName;
	
	public String isPublic;
	
	public String newZipCode;
	
	public String idDetailAddr;
	
	public String etc1;
	
	public String etc2;
	
	public String message;
	
	public String path;
	
	public String host;
	
}

 

public으로 만든 이유는 나중에 접근하기 위해서...

private으로 해도 접근 하는 방식이 있는 걸로 아는데...찾아보기도 귀찬고..ㅠ

 

@Document 라는 어노테이션은

대강 만들었어요!

클래스 이름을 인덱스 이름으로 사용 할 수는 없기에...

 

내용을 보자면..

@Target(TYPE)
@Retention(RUNTIME)
public @interface Document {
	String indexName() default "";
	String typeName() default "_doc";
}

 

ES 6부터는 어차피 인덱스 하나당 타입이 하나고, 해당 타입명은 기본으로 _doc을 사용하기에...

 

 

그리고 입력 삭제 검색 등 기본적인 기능을 다 때려박은 클래스 하나 생성..

public abstract class AbstractRepo <PK extends Serializable, T>{
	
	@Autowired
	private Client client;
	
	private final Class<T> persistentClass;
	
	protected Client getClient() {
		return client;
	}
	
	public AbstractRepo(){
		this.persistentClass =(Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
	}
	
	public UpdateByQueryRequestBuilder updateByQuery() {
		String index = getIndexName(persistentClass);
		UpdateByQueryRequestBuilder updateByQuery =
				  new UpdateByQueryRequestBuilder(client, UpdateByQueryAction.INSTANCE);
		updateByQuery.source(index);
				
		return updateByQuery;
	}
	
	public SearchRequestBuilder searchRequestBuilder() {
		String index = getIndexName(persistentClass);
		SearchRequestBuilder reqBuilder = client.prepareSearch(index)
		.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
		
		return reqBuilder;
	}
	
	public List<T> fullTextSearch(String text) {
		
		List<T> ts = new ArrayList<>();
		
		String index = getIndexName(persistentClass);
		SearchResponse response = client.prepareSearch(index)
		.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
		.setQuery(QueryBuilders.matchQuery("fullTextSearch", text)).get();
		
		SearchHit[] hits = response.getHits().getHits();
		for(SearchHit hit : hits) {
			String jsonString = hit.getSourceAsString();
			ObjectMapper mapper = new ObjectMapper();
			try {
				T t = mapper.readValue(jsonString, persistentClass);
				ts.add(t);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return ts;
	}
	
	public IndexRequestBuilder builder(T t, String routing) {
		String index = getIndexName(persistentClass);
		String type = getTypeName(persistentClass);
		String id = getId(t);
		
		if(id == null) {
			throw new ServiceException("E", "id is null");
		}
		
		IndexRequestBuilder indexRequestBuilder = null;
		
		try {
			ObjectMapper mapper = new ObjectMapper();
			String jsonString;
			jsonString = mapper.writeValueAsString(t);
			
			indexRequestBuilder = client.prepareIndex(index, type, id)
					.setSource(jsonString, XContentType.JSON);
			if(!StringUtil.isBlank(routing)) {
				indexRequestBuilder.setRouting(routing);
			}
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		
		return indexRequestBuilder;
	}
	
	public IndexResponse index(T t, String routing) {
		
		IndexResponse response = null;
		
		try {
			IndexRequestBuilder indexRequestBuilder = this.builder(t, routing);
			response = indexRequestBuilder.get();
		} catch (Exception e) {
			throw new ServiceException("E", "인덱스 생성에 실패하였습니다");
		}
		
			
		return response;
	}
	
	public BulkRequestBuilder bulkRequest() {
		BulkRequestBuilder bulkRequest = client.prepareBulk();
		return bulkRequest;
	}
	
	public BulkResponse bulk(List<T> ts) {
		
		BulkRequestBuilder bulkRequest = client.prepareBulk();

		for(T t : ts) {
			bulkRequest.add(this.builder(t, null));
		}
		
		BulkResponse bulkResponse = bulkRequest.get();
		
		if (bulkResponse.hasFailures()) {
			throw new ServiceException("E", "bulk failure");
		}
		
		return bulkResponse;
	}
	
	public void update(T t) {
		
		String index = getIndexName(persistentClass);
		String type = getTypeName(persistentClass);
		String id = getId(t);
		
		if(id == null) {
			throw new ServiceException("E", "id is null");
		}
		
		try {
			ObjectMapper mapper = new ObjectMapper();
			String jsonString;
			jsonString = mapper.writeValueAsString(t);
		
			UpdateResponse updateResponse = client.prepareUpdate(index, type, id)
					.setDoc(jsonString, XContentType.JSON).get();
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		
	}
	
	public T get(PK id, String routing) {

		String index = getIndexName(persistentClass);
		String type = getTypeName(persistentClass);
		
		GetRequestBuilder request = client.prepareGet(index, type, id.toString());
		
		if(!StringUtil.isBlank(routing)) {
			request.setRouting(routing);
		}
		
		GetResponse response = request.get();
		
		String jsonString = response.getSourceAsString();
		
		if(jsonString == null) {
			return (T)null;
		}
		
		ObjectMapper mapper = new ObjectMapper();
		try {
			T t = mapper.readValue(jsonString, persistentClass);
			return t;
		} catch (IOException e) {
			return (T)null;
		}
		
	}
	
	protected String getId(T t) {
		
		Field[] fields = t.getClass().getDeclaredFields();
		for(Field f : fields) {
			if(f.isAnnotationPresent(Id.class)) {
				
				Object obj;
				try {
					obj = f.get(t);
					return obj==null?null:obj.toString();
				} catch (IllegalArgumentException | IllegalAccessException e) {
					// field를 접근성을 위해 public으로 선언 했으며, 없는게 나올수가 없으므로 위 exception은 발생이 불가능 한거 아닌가... 
					e.printStackTrace();
				}
			}
		}
		
		return null;
	}
	
	protected String getIndexName(Class<T> clazz) {
		
		Document document = clazz.getAnnotation(Document.class);
		return document.indexName();
	}
	
	protected String getTypeName(Class<T> clazz) {
		
		Document document = clazz.getAnnotation(Document.class);
		return document.typeName();
	}
}

 

각각의 Exception처리는 뭐 편하신대로 하시면 더 좋을 거 같아요. 너무 대충해서..

 

참고로 @Id라는 어노테이션은 javax.persistence.Id 입니다.

해당 어노테이션이 있는 필드의 값을 도큐먼트의 _id로 사용하고자 이렇게 하였어요!

 

물론 그걸 알기 위해 위에 클래스에서 reflect.Field를 통한 접근도 하고있죠.

이래서 Docu에서 필드의 접근자를 public을 사용했고요.

 

그럼 이제 각 Docu별로 ES에 접근하기 위한 레파지토리를 만들어 보도록 할게요!

 

@Repository("docuZipcodeRepository")
public class DocuZipcodeRepository extends AbstractRepo<String, DocuZipcode>{
	
	
}

 

내용은~ 

별게 없어요.

이제 이 안의 내용은 사용에 따라 채우시면 될거 같아요.

this.searchRequestBuilder(); 를 사용해서 검색빌더를 생성하고 검색을 하고..

Hits 결과를 문자열로 받아서 오브젝트 매퍼를 이용하여 원하는 클래스로 변환하고...

 

여기서 더 공과 시간을 들이면 편해질지 모르겠으나, 일단 이정도에서 만족하며...

저는 그럼 일을 하도록 하겠습니다~ ㅋㅋㅋ

 

일을 위한 일을 하느라 시간을 쓸데 없이 많이 쓴듯...

https://www.elastic.co/kr/blog/security-for-elasticsearch-is-now-free

 

Security for Elasticsearch is now free

Elastic Stack의 핵심 보안 기능이 이제 무료 제공된다는 기쁜 소식을 전해 드립니다.

www.elastic.co

 

ES 6.8 과 7.1 부터 xpack의 일부 특히 중요한 인증 기능이 무료 입니다요~!!

https://www.elastic.co/kr/subscriptions

 

구독· Elastic Stack 제품 및 지원 | Elastic

시작부터 개발과 프로덕션까지, 모든 단계에서 저희가 함께 합니다. 작은 규모로 시작해 큰 규모로 출범시키는 작업 과정이 어떤지 저희는 잘 알고 있습니다. 처음부터 바로 Elastic 스타트업 프로그램과 함께 시작해 개발을 거쳐 프로덕션에 이르기까지 Elastic 제품, 기능 및 지원을 이용해 보세요.

www.elastic.co

 

자세한 정책은 위에서~ ㅎㅎ

 

 

 

항상 키바나를 사용하며 불편한 점이 있는게...

이걸 도메인과 연결시키면 별다른 인증절차 없이 접근이 되니까...ㅠㅠ

방화벽에 ip로 걸어서 야금야금 사용했어요..

괜히 오픈했다가 누구한테는 별거 아닌 데이터지만 회사자료를 공개 할 수도 있으니까요~~

 

근데 이제 인증 절차가 생겻으니 ㄴㅇㅅ 죠~

 

(ㄴㅇㅅ는 나이스에요. 게임하다 초딩들한테 배웠어요. 첨에 애들이 다 ㄴㅇㅅ ㄴㅇㅅ 하는데 욕하는줄.)

 

 

ES 7.1 설치하시면 x-pack 플러그인은 기본적으로 설치되어 있습니다.

 

elasticsearch.yml에서 

xpack.security.enabled: true

xpack.security.transport.ssl.enabled: true

(ssl관련 설정을 추가적으로 해야할것들이 있나봐요~ ES올라갈때 핸드쉐이크 어쩌고 막 오류가 나는데 안되는건 아닌지라 패스~)

설정해주시고

ES 실행 후~

 

bin/elasticsearch-setup-passwords interactive

 

실행하시면 Elastic Stack에 관련된 제품들 기본사용자에 대한 비밀번호를 설정 할 수 있어요.

(이유는 잘 모르겠지만, 비밀번호 설정시 특수문자를 넣지 마세요)

 

그리고 kibana 또한 kibana.yml에서 

 

아래 항목을 입력해 주세요.

 

elasticsearch.username: "kibana"
elasticsearch.password: "키바나비밀번호"

 

위에서 설정한 kibana에 대한 값을 넣어주시면 돼요.

 

그리고 키바나를 재실행 하면~~

 

이런 로그인 화면이 나와요~ 

그리고 ES를 브라우저로 접속할때에도 

이런 창이 뜨네요~

 

 

거기에다 추가적으로 권한 및 사용자 생성이 가능하니 참고하세요~

Nori 설치는 공식 플러그인이다보니 너무 쉽게 변했죠.

이번엔 이걸 이용해서 인덱스 설치를ㄹㄹㄹ...

 

노리에 대한 정보는...

https://www.elastic.co/guide/en/elasticsearch/plugins/7.0/analysis-nori-analyzer.html

 

nori analyzer | Elasticsearch Plugins and Integrations [7.0] | Elastic

 

www.elastic.co

 

위 링크 가시면  필터와 토크나이저에 대한 링크가 있으며, 해당 링크에는 예제와 설명들이 있습니다.

 

저는 저걸 섞어서 인덱스 하나만 만들어 보도록 할게요. 

 

작업은 Kibana > Dev Tools > Console 에서 진행하였습니다.

curl로 작업시 아래 부분이 필요하겠지용?

curl -H 'Content-Type: application/json' -X PUT http://127.0.0.1:9200/...

 

예제에 들어가기에 앞서 간략한 설명.

 

decompound_mode : 단어를 어떻게 분리 하는지...

 - none, discard, mixed 세가지가 있고요.

 - none : 분리안함.

 - discard : 기본값이고요. 단어를 사전에 있는 단어 기준으로 분리하는거에요.

 - mixed : 위 두가지 혼합. 원래 단어(조사?맞나? 제외)와 사전 기준 쪼개진 단어를 분리하는 두 결과 모두를 출력.

 

nori_readingform : 한문을 한글로 변환해주는 친구

- REQUEST
GET /index01/_analyze
{
  "analyzer": "korean",
  "text" : "金"
}

- RESULTS
{
  "tokens" : [
    {
      "token" : "김",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "word",
      "position" : 0
    }
  ]
}

lowercase : 영문자를 소문자로 해주는 친구

 

nori_part_of_speech : 품사 태그 배열, 즉 stoptags에 포함된 걸 만나면 제거해서 결과를 주는 그런 친구인데...

저 안에 배열이 정확히 뭘 의미하는지 궁금하다면 ...

http://lucene.apache.org/core/8_0_0/analyzers-nori/org/apache/lucene/analysis/ko/POS.Tag.html

 

POS.Tag (Lucene 8.0.0 API)

values() Returns an array containing the constants of this enum type, in the order they are declared.

lucene.apache.org

위 페이지 한글로 번역하기 돌리면 아주 잘 나와요.

저는 공식 문서에 있는 예제 그냥 갖다 붙인...ㅎㅎ

그리고 NR에 대한 예제가 있던데, 이건 숫자로 명사를 꾸미는 경우 숫자를 표현한 한글을 제거해주더라고요!

 


- REQUEST
PUT /index01
{
  "settings": {
    "number_of_shards" : 2,
    "number_of_replicas" :0,
    "index": {
      "analysis": {
        "tokenizer": {
          "nori_tokenizer_mixed": {
            "type": "nori_tokenizer",
            "decompound_mode": "mixed"
          }
        },
        "analyzer": {
          "korean": {
            "type": "custom",
            "tokenizer": "nori_tokenizer_mixed",
            "filter": ["nori_readingform", "lowercase", "nori_part_of_speech_basic"]
          }
        },
        "filter" : {
          "nori_part_of_speech_basic": {
            "type" : "nori_part_of_speech",
            "stoptags" : [
              "E",
              "IC",
              "J",
              "MAG", "MAJ", "MM",
              "SP", "SSC", "SSO", "SC", "SE",
              "XPN", "XSA", "XSN", "XSV",
              "UNA", "NA", "VSV"
            ]
          }
        }
      }
    }
  }
}

- RESULTS
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "index01"
}

 

생성된 인덱스를 확인~

- REQUEST
GET /index01

- RESULTS
{
  "index01" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "index01",
        "creation_date" : "1556261305395",
        "analysis" : {
          "filter" : {
            "nori_part_of_speech_basic" : {
              "type" : "nori_part_of_speech",
              "stoptags" : [
                "E",
                "IC",
                "J",
                "MAG",
                "MAJ",
                "MM",
                "SP",
                "SSC",
                "SSO",
                "SC",
                "SE",
                "XPN",
                "XSA",
                "XSN",
                "XSV",
                "UNA",
                "NA",
                "VSV"
              ]
            }
          },
          "analyzer" : {
            "korean" : {
              "filter" : [
                "nori_readingform",
                "lowercase",
                "nori_part_of_speech_basic"
              ],
              "type" : "custom",
              "tokenizer" : "nori_tokenizer_mixed"
            }
          },
          "tokenizer" : {
            "nori_tokenizer_mixed" : {
              "type" : "nori_tokenizer",
              "decompound_mode" : "mixed"
            }
          }
        },
        "number_of_replicas" : "0",
        "uuid" : "xdcxjktjSSeoVUtFniPkSg",
        "version" : {
          "created" : "7000099"
        }
      }
    }
  }
}

 

그리고 간단한 문장 분석~

- REQUEST
GET /index01/_analyze
{
  "analyzer": "korean",
  "text" : "아버지가 방에 들어오신다"
}

- RESULTS
{
  "tokens" : [
    {
      "token" : "아버지",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "방",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "들어오",
      "start_offset" : 8,
      "end_offset" : 11,
      "type" : "word",
      "position" : 4
    }
  ]
}

 

그리고 index에서 shards를 2로 했는데요.

샤드에 관한건 

https://brownbears.tistory.com/4

 

노드 생성, 동작 원리 및 shard란?

1) 노드의 생성 및 동작 원리 사용자가 하나의 머신에서 Elasicsearch를 시작하게 되면, 하나의 Elasticsearch 노드가 생성되며, 이 노드는 동일한 네트워크 상에서 같은 클러스터명을 같는 클러스터가 존재하는..

brownbears.tistory.com

위에 링크 보시면 샤드에 대해 설명을 잘 해주셨더라고요. 

 

저는 어차피 단일 노드라서 ㅎㅎ 

샤드와 성능의 상관 관계는 많은 글들이 있지만, 

샤드는 많아질수록 빨라지다 느려지고, 하더라고요? 적정선을 유지하는게 좋다고 하는데..

샤드 수는 코어에 관련된 공식도 있던데...

위 글 보고 노드1개가 별 다른 설정없으면 샤드5개 라길래 1개보단 2개가 좋겠지 하고 2로 했어요.

 

그럼 저는 이만 총총총...

Tomcat의 catalina.out 을 filebeat를 통해 logstash로 전송 후 이걸 ES에 저장.

그리고 키바나에서 분석 및 기타 등등 하고 싶은 작업을 하는...

 

이걸 하게된 계기는 그냥 유부트 영상보다가 하게 된...

 

https://www.youtube.com/watch?v=J2PIBQgEpC4&list=PLVNY1HnUlO24LCsgOxR_eK2Yi4sOgH9Pg

허민석 님의 유투브 강의 인데, 이것만 보면 나도 이제 데이터사이언티스트 ㅋㅋㅋㅋ라고 생각하며 정독 했어요.

 

이 영상은 5.6 기반의 영상인데 나는 7.0 환경에서 하다보니 약간의 문제 아닌 문제들을 만나게 되었어요.

 

일단 E가 실행중이어야 L이 데이터를 보낼 수 있으며, L 또한 실행중이어야 F가 데이터를 보낼 수 있겠죠?

 

E는 그냥 켜있기만 하면 되고요!

 

L은 input과 output에 대한 정보가 기록된 conf 파일이 필요해요.

 

모든 내용을 다 알어야 하는건 아닌데

 

output 부분에서 if를 사용가능하더라고요.

output {
  if [host][hostname] == "www1" or [host][hostname] == "www2" {
    elasticsearch {
      hosts => ["http://localhost:9200"]
      manage_template => false
      index => "front-%{+YYYY.MM.dd}"
      document_type => "%{[@metadata][type]}"
    }
  }
  else if [host][hostname] == "batch" {
    elasticsearch {
      hosts => ["http://localhost:9200"]
      manage_template => false
      index => "batch-%{+YYYY.MM.dd}"
      document_type => "%{[@metadata][type]}"
    }
  }
  else {
    elasticsearch {
      hosts => ["http://localhost:9200"]
      manage_template => false
      index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}"
      document_type => "%{[@metadata][type]}"
    }
  }
}

 

if 에 조건부에 쓰인 부분이 정확히 뭔지도 모르고 그냥 예제 따라 했더니,

자꾸 else로 빠져서 내가 원하는 인덱스에 데이터를 안넣더군요!

 

그러다가 E에 쌓인 데이터를 보니 ...

조건절에 쓰인내용을 필드를 가리키는 거였...뭔지도 잘 모르고 계속 해보니 안될 수 밖에...

무슨 차이인지 모르겠으나(버전차이?) 필드 이름이 예제와는 다르게 넘어오고 있던...

 

그리고 filebeat는 tomcat이 설치된 서버에 설치를 하는데...

dpkg 설치를 해서 설정 파일은 /etc/filebeat에 있어요.

filebeat.yml을 열어보면

 

filebeat.inputs 와 output.logstash

부분을 수정 하면 되는데요.

 

일단 inputs 부분은

enabled 부분을 true로 변경하고

paths 부분을 톰캣의 catalina.out으로 변경하면 되겠죠?

 

그리고 예제에서는 멀티라인이 알아서 잘 처리가 되던데...버전에 따른 기본값의 차이인지 모르겠으나, 

저 부분이 주석이 걸린 상태에서는 작동이 안되어서, 한줄한줄 도큐멘트로 다 쌓이는...ㅎㅎ

순식간에 용량이 어마어마 해지더라고요~!!

 

pattern 부분은 본인 로그에 알맞게 수정해서 사용하면 될거 같아요!

보통 톰캣로그가 시작부분이 [시간]으로 시작을 해서 저렇게 하면 잘 처리되는듯 합니다!

 

그리고 두번째로 만난 문제!

curator에 대한 부분인데요. curator가 5.6버전까지만 있어요.

근데 뭣도 모르고 이거 설치하고 사용하려고 하니 버전차이로 인해 안되는거 있죠.

 

그래도 오래된 쓸모없는 로그자료는 지워야 하기에...찾아보니...

https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-index-lifecycle-management.html

 

Getting started with index lifecycle management | Elasticsearch Reference [7.0] | Elastic

Getting started with index lifecycle managementedit Let’s jump into index lifecycle management (ILM) by working through a hands-on scenario. This section will leverage many new concepts unique to ILM that you may not be familiar with. The following section

www.elastic.co

 

이런게 있더라고요!

 

일단 정책? 을 만들어주고, 그 정책을 사용할 템플릿을 만들어 주고 해당 템플릿에는 여러 인덱스가 속하겠고요.

그럼 알아서 작동하는건데~~

기존에 있던 인덱스는 적용이 안되더라고요? 이 부분은 저도 잘 모르겠어요. 좀더 찾아봐야 할까 했는데...

어차피 새로 만드는 인덱스만 적용되면 저는 만족 하는 부분이라...ㅎㅎ

 

Kibana DEV Tool Console

 

정책은 위처럼도 가능하지만, Kibana > Management > Index Lifecycle Policies에서도 작성이 가능해요

작성한 정책을 인덱스템플릿과 연결하는것도 kibana에서 가능합니다.

 

그리고 직면하는 또 새로운 문제 ㅋㅋㅋㅋㅋ

 

https://discuss.elastic.co/t/index-lifecycle-error/171254/4

 

Index lifecycle error

I have the same error. @dakrone the indices are being created by Metricbeat. The template does not contain an alias: { "metricbeat-6.6.0": { "order": 0, "index_patterns": [ "metricbeat-6.6.0*" ], "settings": { "index": { "lifecycle": { "name": "Metricbeat"

discuss.elastic.co

이 부분은 공식문서에도 나오는 부분인데 인덱스를 alias를 지정해야하는 문제다.

근데 인덱스가 logstash를 통해 데이터를 받으며 자동으로 생성이 되는건가 어찌해야할까...

위 내용을 잘 읽어보면 해결책이 있다.

 

처음에 한번만 수동으로 인덱스를 생성하면 문제가 안된다고 하네요.

공식문서에도 비슷한 내용이 적혀있긴한데 영알못인 나한테는 너무 어렵게 다가옴...

 

그리도 또 한가지 문제를 만남...

 

 

인덱스 이름의 패턴은 -뒤에 숫자만 가능...하다는...그렇다면 . 빼고 숫자만 남겨주면 되려나?..

+ Recent posts