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

했었던 적이 있었어요.

 

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

한 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

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

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

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

 

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

 

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

다시 아래 글을 보았어요.

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를 통해 데이터를 받으며 자동으로 생성이 되는건가 어찌해야할까...

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

 

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

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

 

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

 

 

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

# 키바나와 ES는 버전이 동일해야해요. 마이너 버전까지 동일해야하는지는 안해봐서 모르겠지만 여튼 그래요.

# 7버전 설치하려고 ES도 7버전으로 설치했어요.

# ES 7은 프로덕션모드로 실행하기 위해서 discovery.seed_hosts와 cluster.initial_master_nodes를 지정해야해요.

 

키바나는 아래 링크에 잘 설명되어 있어요.

https://www.elastic.co/kr/products/kibana

 

kibana: 데이터 탐색, 시각화, 발견 | Elastic

메뉴에서 시계열 분석도 제공합니다. Kibana의 시계열 UI를 통해 Elasticsearch 데이터에 대한 상세 시계열 분석을 실시해 보세요. 강력하고 쉽게 배울 수 있는 표현식으로 쿼리와 데이터 변화, 시각 자료를 설명할 수 있습니다.

www.elastic.co

시각화 도구도 제공해주고 개발자 도구도 제공해 준답니다.

개발자 도구 완전 중요하죠! 콘솔에서 날리는것보다 훨씬 편하겠네요!~

 

자 그럼 설치는 어떻게 할까요?

https://www.elastic.co/kr/downloads/kibana

 

Download Kibana Free • Get Started Now | Elastic

Want to upgrade? We'll give you a hand. Migration Guide »

www.elastic.co

각 OS에 맞는 설치방법이 다양하게 존재 합니다만...

저는 DEB 64-BIT 라고 써있는걸 받아서 할예정이에요.

관련 문서는 

https://www.elastic.co/guide/en/kibana/current/deb.html

 

Install Kibana with Debian Package | Kibana User Guide [7.0] | Elastic

An alternative package, kibana-oss, which contains only features that are available under the Apache 2.0 license is also available. To install it, use the following sources list: echo "deb https://artifacts.elastic.co/packages/oss-7.x/apt stable main" | su

www.elastic.co

 

> wget https://artifacts.elastic.co/downloads/kibana/kibana-7.0.0-amd64.deb
> sudo dpkg -i kibana-7.0.0-amd64.deb
Selecting previously unselected package kibana.
(데이터베이스 읽는중 ...현재 61637개의 파일과 디렉터리가 설치되어 있습니다.)
Preparing to unpack kibana-7.0.0-amd64.deb ...
Unpacking kibana (7.0.0) ...
kibana (7.0.0) 설정하는 중입니다 ...
Processing triggers for ureadahead (0.100.0-16) ...

요렇게 하면 일단 설치는 완료!

 

간략하게 필요한 설정부분을 확인해볼게요!

/etc/kibana

kibana.yml이 설정파일 인듯 합니다.

파일을 열어보니 113줄로 되어있는데요. 활성화된 줄은 하나도 없고 전부 주석 처리 되어 있네요.

여기서 잠깐 간략 설치 과정 살펴 볼게요.

다른 설정은 몰라도 ES주소는 꼭 입력해야하나보군요!

해당 내용은 28번째 줄에 위치하고 있는데요. 저는 같은서버에 설치하는거라서 주석만 풀어주면 될거 같아요.

그리고 추가적으로 ES와 마찬가지로 외부접속이 가능하려면 host에 대한 내용도 수정해야해요.

위 내용도 마찬가지로 주석을 풀고 "0.0.0.0"으로 해주면 되겠죠?

기타 다른 설정에 대한 내용들은 아래 링크를 참고 하세요!

https://www.elastic.co/guide/en/kibana/current/settings.html

 

Configuring Kibana | Kibana User Guide [7.0] | Elastic

The Kibana server reads properties from the kibana.yml file on startup. The location of this file differs depending on how you installed Kibana. For example, if you installed Kibana from an archive distribution (.tar.gz or .zip), by default it is in $KIBAN

www.elastic.co

자 그럼 실행 해볼게요

> service kibana start
kibana started

짝짝짝! 실행완료!

키바나 실행관련 로그는 /var/log/kibana 에서 확인 가능해요.

tail -fn 100 /var/log/kibana/kibana.stdout

웹사이트를 돌아다니다보면 ㄱㄴㄷㄹ...순서로

뭔가를 필터 하는 기능이 가끔 있어요.

 

자음 ㄱ으로 시작하는 것에 대해서 나타내려면 어떻게 해야하지...고민을 하긴 개뿔 검색을 해보았죠.

내가 고민하고 생각하는건 이미 다 누군가가 만들어 놨을 테니까!!

 

그러다가 이 글을 보았어요.

https://blog.naver.com/PostView.nhn?blogId=tmondev&logNo=220918935030

 

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

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

blog.naver.com

와~ 너무 고마운거 있죠?

그리고 글 시작 부분에 아래와 같은 글도 있더라고요~. 넘흐 조하

(다 읽어보니 너무 유익해요! 저 같은 ES허접은 짱짱 유익해요!)

(1) 어떠한 검색어도 의미있는 추천을 
(2) 핫딜 검색순위 조절하기

[출처] ES플러그인 커스터마이징 (3)ㅎㅇㅇㅎ검색 대응하기|작성자 개발몬스터

 

결정적으로 초성검색이 가능한 플러그인을 만드는건데...

저는 초성검색이 가능하면 좋겠지만! 일단은 시작글자 초성기반 필터링을 목적으로 하는거라서요 ㅎㅎ(허접해)

그래서 고심끝에 아주 쉬운 방법으로 접근을...ㅎㅎ

목적 단어에서 시작 글자 초성을 뽑아내서 저장해서 걔로 필터링을 하면 되겠지 하고 생각을 했죠. 므흣.

import java.util.ArrayList;
import java.util.List;

public class ChosungTest {
	// 한글 소리 마디의 Unicode 시작 지점 (가)
	static char UNICODE_SYLLABLES_START_CODEPOINT = 0xAC00;
	
	// 한글의 Unicode 총 글자 수
	static int COUNT_IN_UNICODE = 11172;
	
	// 한글 중성의 Unicode 총 글자 수
	static int COUNT_JUNGSUNG_IN_UNICODE = 21;
	
	// 한글 종성의 Unicode 총 글자 수
	static int COUNT_JONGSUNG_IN_UNICODE = 28;
	
	// 한글 자모 분해의 계산 기본 값 (중성 글자 수 * 종성 글자 수)
	static int JAMO_SPLIT_VALUE = COUNT_JUNGSUNG_IN_UNICODE * COUNT_JONGSUNG_IN_UNICODE;

	protected final static char[] COMPATIBILITY_CHOSUNGs = {  
				            0x3131, 0x3132, 0x3134, 0x3137, 0x3138,     // ㄱ, ㄲ, ㄴ, ㄷ, ㄸ  
				            0x3139, 0x3141, 0x3142, 0x3143, 0x3145,     // ㄹ, ㅁ, ㅂ, ㅃ, ㅅ  
				            0x3146, 0x3147, 0x3148, 0x3149, 0x314A,     // ㅆ, ㅇ, ㅈ, ㅉ, ㅊ  
				            0x314B, 0x314C, 0x314D, 0x314E              // ㅋ, ㅌ, ㅍ, ㅎ  
				    };  
	
	public static char parse(String token) {
		char rtn = ' ';
		
		if(null == token) {
			return rtn;
		}
		
		token = token.trim();
		
		for (int i = 0, length = token.length(); i < length; i++) {
			char ch = token.charAt(i);
			
			// 이게 뭐하는거지 하고 보니까 한글 범위에 대한 값을 찾는 역할 이더라고요. 결과가 0~11172 사이의 값이 나오면 한글인거죠!
			char expectedKorean = (char) (ch - UNICODE_SYLLABLES_START_CODEPOINT);

			System.out.println(ch);
			System.out.println(expectedKorean);
			
			if (expectedKorean >= 0 && expectedKorean <= COUNT_IN_UNICODE) {  
				System.out.println("한글");
				
				int chosung = expectedKorean / JAMO_SPLIT_VALUE;
				
				System.out.println(COMPATIBILITY_CHOSUNGs[chosung]); 
				
				rtn = COMPATIBILITY_CHOSUNGs[chosung];
				break;
			}else {
				System.out.println("노한글");
				rtn = ch;
				break;
			}
		}
		return rtn;
	}
	
	public static void main(String[] args) {
		List<Character> ls = new ArrayList<>();
		
		ls.add(parse("한국"));
		ls.add(parse("korean"));
		ls.add(parse("韓國"));
		ls.add(parse("케이리그"));
		ls.add(parse("k리그"));
		ls.add(parse(""));
		
		System.out.println(ls);
        // 결과 : [ㅎ, k, 韓, ㅋ, k,  ]
	}

}

 

중간 중간 값이 궁금해서 출력문을 넣었어요.

그리고 ...중요한건 초성 분리 공식부분인데..

static int JAMO_SPLIT_VALUE = COUNT_JUNGSUNG_IN_UNICODE * COUNT_JONGSUNG_IN_UNICODE;
...
int chosung = expectedKorean / JAMO_SPLIT_VALUE;

이게 뭐하는거지~ 하고 잘 이해가 안갔어요.

그래서 글을 좀 찾아보았죠.

http://dream.ahboom.net/entry/%ED%95%9C%EA%B8%80-%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C-%EC%9E%90%EC%86%8C-%EB%B6%84%EB%A6%AC-%EB%B0%A9%EB%B2%95

 

IT 소시민의 소소한 이야기 :: 한글 유니코드 자소 분리 방법

한글 유니코드 자소 분리 방법 한글 유니코드 자소 분리하는 방법을 설명해 보겠습니다. 이제는 많이 알려진 방법이라 드문 내용은 아니지만 제가 알고 있는 지식을 정리하는 차원에서 정리해 봤습니다. 물론 완성된 코드만 사용하셨던 분이거나 처음 접하는 분이라면 이론적 배경을 아시게 될 겁니다. 언어는 자바스크립트를 사용하도록 하겠습니다. 자바스크립트의 문자열은 내부적으로 16비트 유니코드로 처리되고 있기 때문에 여기서 설명하는 원리를 바로 테스트 해 보기 좋습

dream.ahboom.net

이게 분리하는거부터 보면 이해가 어렵지만 위 포스팅 글 내용처럼 합치는걸 먼저 보니 아하! 하고 이해가 가더라고요!

대강 어떤 느낌인지 알았으니까 된거겠죠?

 

결과적으로 이렇게 나온 초성 첫글자 데이터를 저장해서 필터로 사용하면 문제가 안 될거 같아요!

굳이 뭐 ES가 아니어도 상관없는건데...검색기능도 만들어서 나중에 붙이려고 이렇게 하고 있네요.

 

그때는 위 링크들을 바탕으로 플러그인 만들어서 사용해야지~

 

도움주신분들 너무 감사합니다!

# ES 7은 프로덕션모드로 실행하기 위해서 discovery.seed_hosts와 cluster.initial_master_nodes를 지정해야해요.

 

오래된 글이긴한데, 검색을 하다 보았어요.

https://www.elastic.co/kr/blog/nori-the-official-elasticsearch-plugin-for-korean-language-analysis

 

공식 한국어 분석 플러그인 “노리” | Elastic

얼마 전에 “어떤 한국어 분석기를 사용할까”라는 블로그 포스트를 발표한 적이 있습니다. 이 기사는 Elasticsearch 클러스터에 설치해서 한국어 지원을 강화할 수 있는 세 가지 서드파티 플러그인을 소개했습니다. 오늘 우리는 Elasticsearch 6.4.0에서 이 인기 있는 언어를 처리하는 동종 최고의 공식 플러그인 analysis-nori를 발표하게 되어 무척 기쁩니다. 이 블로그 포스트를 통해, 한국어 텍스트 분석을 처리하는 플러그인이 사용하는

www.elastic.co

왜 썸네일이 김이지...(베이비복스 팬인가...하하하)

관련된 다른 글들을 찾아보니, 일본어 형태소 분석기 엔진에 MeCab 사전을 바탕으로 만들었다고...

 

일단 ES 최신버전을 받아서 설치를 해봐야지

> wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.7.1.tar.gz
> tar -xvf elasticsearch-6.7.1.tar.gz
> chown -R search:search elasticsearch-6.7.1
> bin/elasticsearch-plugin install analysis-nori

 

너무 간단해...

추억의 아버지가방에들어갔다 테스트

> curl -X POST http://127.0.0.1:9200/_analyze?pretty -H 'Content-Type: application/json' -d '{
>  "analyzer":"nori",
>  "text":"아버지가방에들어갔다"
> }'
{
  "tokens" : [
    {
      "token" : "아버지",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "가방",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "들어가",
      "start_offset" : 6,
      "end_offset" : 9,
      "type" : "word",
      "position" : 3
    }
  ]
}


> curl -X POST http://127.0.0.1:9200/_analyze?pretty -H 'Content-Type: application/json' -d '{
>  "analyzer":"nori",
>  "text":"아버지가 방에 들어갔다"
> }'
{
  "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
    }
  ]
}

아버지가 방에 들어간건지 가방에 들어간건지..ㅎㅎ

한글은 띄어쓰기에 따라 뜻이 달라질수 있으니, 요래 복잡한가 봐요.

 

어쨋든 공식 플러그인 이다 보니, 설치가 너무 간편하네요.

 

개발모드와 운영모드에 대한건 뭐 동일할테고, 

인덱스 생성시 사용자 단어 사전 추가하는건 다른 분들의 글을 찾아보세요.

 

# ev 6.7.0에 pv 6.1.1.1은 설치는 가능한대, 작동이 안되요! 아래 글은 삽질 과정이 포함되어 있습니다!

# 6.7.0으로 시도했다가 안되서 6.1.4로 재시도 하였습니다.

ES 7은 프로덕션모드로 실행하기 위해서 discovery.seed_hosts와 cluster.initial_master_nodes를 지정해야해요.

 

찾아보자 찾아보자~~

1. ElasticSearch 설치

 

일단 현재 ES 최신버전은 6.7.0

 

https://www.elastic.co/kr/downloads/elasticsearch

저장소 등록 후 apt-get 또는 deb를 내려받아서 패키지 설치를 CentOS라면 rpm 설치 하시면 되겠지용?

하지만, 저는 

https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.7.0.tar.gz 

위 링크를 받아서 그냥 압축 풀어서 사용할게요.

그냥 압축풀고 실행만 하면 되서 어려울게 없어요.

뭔가 실수 했을때 지우기도 편하고 ㅎㅎ(이게 핵심)

 

> wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.7.0.tar.gz
> tar -xvf elasticsearch-6.7.0.tar.gz

위와 같이 명령어를 입력하면

아래처럼 파일 하나 받고, 압축 풀어서 폴더도 생기겠죠?

결과

 

> cd elasticsearch-6.7.0

폴더 내용을 살펴 볼까용?

결과

아~ /bin은 실행파일 있을거고, /config는 설정관련, /logs는 로그가 쌓이겠다~ 그죠?

 

/bin

 

/config

 

이제 어떻게 실행하는지...

공식홈을 한번 볼까요?

https://www.elastic.co/kr/downloads/elasticsearch

아주 친절하죠~

시키는대로 해보죠!

 

> bin/elasticsearch

앗! 이럴수가!

저는 루트 계정이라 실행이 안되네요 ㅠ_ㅠ

계정을 변경해야겠어요.

 

> adduser search

search라는 이름의 계정을 만들었어요!

(그리고 필요할지 모르니 sudo 권한을 주세요!)

이제 폴더와 파일의 소유권을 search계정이 사용 할 수 있게 만들어 봅시다.

 

일단 상위 폴더로 먼저 이동 하고...

 

> cd ..
> chown -R search:search elasticsearch-6.7.0
> ll

소유자 및 그룹 변경 완료!

자 그럼 이제 계정을 search로 갈아타서 실행해볼게요!

> su - search
> bin/elasticsearch

실행결과

뭐라고 말이 많은데 실행이 된거 같죠? 첫 줄에 경고는 찾아봐야 겠네요.

(위 상태에서 Ctrl+c 누르면 stop이 됩니다!)

 

앗 그리고 중요한거! 자바가 설치되어 있어야해요! 1.8버전 이상의 자바요!

(이걸 이제서야 말하다니!!)

오라클 자바는 구독해야하니까...(맞죠? 요즘 오라클자바쓰는분들 돈내고 쓰시는거 맞죠?)

지금 설치하고 있는 서버에는 zulu8-openjdk 설치되어 있습니다~

 

일단 기본 port는 9200일테니, 한번 웹으로 접속 해볼게요!

 

ip가 노출되서...여튼 접속이 안되요! 왜 안될까요?!

그럼 정상적으로 실행이 되어 있는지 확인 이라도 해볼까요?

> curl http://localhost:9200
{
  "name" : "GryxpCj",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "_N9jkX5iTfKrYEYBfpIzjw",
  "version" : {
    "number" : "6.7.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "8453f77",
    "build_date" : "2019-03-21T15:32:29.844721Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

와 아주 정상적이에요!

근데 왜 웹으로는 안될까요?

 

접속이 왜 안되는지 확인 해봐야겠어요.

아마 설정 부분일거 같은데요~~

> vi config/elasticsearch.yml

# ---------------------------------- Network -----------------------------------
#
# Set the bind address to a specific IP (IPv4 or IPv6):
#
#network.host: 192.168.0.1
#
# Set a custom port for HTTP:
#
#http.port: 9200
#
# For more information, consult the network module documentation.
#

제 기억으로는 이 부분을 수정하면 되는 것으로 기억을 합니다.

http.port 는 서버가 실행되는 port를 말하겠죠~?

network.host 는 접속 할 수 있는 IP 대역대를 말하는것 같아요!!

 

뭐 실 방화벽 및 웹 방화벽 사용하실테니까~~ 저는 그렇기 때문에 network.host 는 0.0.0.0으로 해줄거에요!

그리고 아시겠지만, 앞에 #은 주석이니까 꼭 제거 해주셔야 해요. 

 

그럼 다시 실행 해보면~~아 또 안되~~~

 

ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

영어는 잘 못 하지만, 이 정도는 읽을 수 있찌!!

가상 메모리 영역이 최소 262144 정도는 되야 한다는 얘긴데..

 

레퍼런스 문서를 찾아보면...

https://www.elastic.co/guide/en/elasticsearch/reference/current/system-config.html

 

Important System Configuration | Elasticsearch Reference [6.7] | Elastic

Important System Configurationedit Ideally, Elasticsearch should run alone on a server and use all of the resources available to it. In order to do so, you need to configure your operating system to allow the user running Elasticsearch to access more resou

www.elastic.co

elasticsearch는 기본적으로 개발 모드로 작동을 하는데 네트워크 구성을 적용하면 프로덕션 모드로 작동을 한다고 하네요. 이 과정에서 아래의 것들중 부족한게 있으면 안된다고...

 

위 링크 들어가서 하나하나 다음 링크를 눌러보면..

https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html

 

Virtual memory | Elasticsearch Reference [6.7] | Elastic

Elasticsearch uses a mmapfs directory by default to store its indices. The default operating system limits on mmap counts is likely to be too low, which may result in out of memory exceptions. On Linux, you can increase the limits by running the following

www.elastic.co

요기로 들어가지는데요!

아래 명령어를 실행하라고 하네요!

> sysctl -w vm.max_map_count=262144

일단 위 사항 때문에 실행이 안되니까, 위 명령어를 실행 후 다시 한번 bin/elasticsearch를 해보겠어요!

 

와 드디어 실행이! 되었어요!

로그에서 달라진 점이 있다면 publish_address와 bound_address가 달라졌다는거...

이제 웹에서 접속이 되는지 볼게요!

 

사무실에 개발서버로 비치된 내부IP는 192.168.0.18 입니다.

그래서 http://192.168.0.18:9200/ 로 접속.

접속이 잘 되네요!

 

2. 은전한닢 플러그인 적용

이제 은전한닢을...적용 해볼까요.

(너무 길어요 ㅠㅠ. 언제 끝날까 이 포스팅..)

 

일단 너무 고마운 은전한닢 프로젝트 블로그로 가볼게요.

http://eunjeon.blogspot.com/

 

          은전한닢 프로젝트

은전한닢 프로젝트: 오픈 소스 한국어 / 한글 형태소 분석기 Lucene/Solr, ElasticSearch 플러그인

eunjeon.blogspot.com

(플러그인 개발이 주업이 아닐텐데 이런 수고스런 일을 해주시다니, 정말 감사합니다.ㅠㅠ)

 

글을 좀 내려보니 이런 글이 있어요!

http://eunjeon.blogspot.com/2017/06/elasticsearch-anaysis-seunjeon.html

 

elasticsearch-anaysis-seunjeon 다운로더

은전한닢 프로젝트: 오픈 소스 한국어 / 한글 형태소 분석기 Lucene/Solr, ElasticSearch 플러그인

eunjeon.blogspot.com

 

참 감사합니다 ㅠ

예전에 ES 버전에 맞춰서 은전한닢을 설치했던 것이 기억에 납니다.

그렇기 때문에 이러한 작업을 해주신게 아닐까 해요!

 

설치한 ES 는 6.7.0 

elasticsearch-anaysis-seunjeon은 6.1.1.1 입니다

 

plugin download 명령은

bash <(curl -s https://bitbucket.org/eunjeon/seunjeon/raw/master/elasticsearch/scripts/downloader.sh) -e 6.7.0 -p 6.1.1.1

 

이렇게 수정이 되겠죠?

-e 는 es버전 -p는 plugin버전

downloader.sh 내용이 궁금해서 받아서 열어 보았습니다.

위에 설명해주신 내용과 별반 다를게 없는 내용이었습니다.

 

> bash <(curl -s https://bitbucket.org/eunjeon/seunjeon/raw/master/elasticsearch/scripts/downloader.sh) -e 6.7.0 -p 6.1.1.1
...
...
./downloader.sh: line 74: zip: command not found

 

일단 실행을 했는데, 안되네요. zip이 설치가 안되어 있어서 그렇네요.

뭐 대충 apt-get install zip 하면 설치 되고, 다시 시도 했어요.

임시폴더에 받아서 버전 변경 작업을 하고 현재 작업 폴더로 가져오는 거에요.

 

아래처럼 실행하면,

> bin/elasticsearch-plugin install file://`pwd`/elasticsearch-analysis-seunjeon-6.1.1.1.zip
warning: Falling back to java on path. This behavior is deprecated. Specify JAVA_HOME
-> Downloading file:///mmv/search/elasticsearch-6.7.0/plugins/elasticsearch-analysis-seunjeon-6.1.1.1.zip
[=================================================] 100%  
ERROR: This plugin was built with an older plugin structure. Contact the plugin author to remove the intermediate "elasticsearch" directory within the plugin zip.

에러가 나요!

 

이 플러그인은 예전 방식 구조 라서, zip내에 elasticsearch 폴더를 지우래요.

압축 해제 후 필요한 파일만 포함해서 재압축 해야겠어요.

그리고 다시 실행!

 

성공적으로 설치 되었네요~!

 

ES를 재기동 하면

[2019-04-05T17:34:46,792][INFO ][o.e.p.PluginsService     ] [eC7q10w] loaded plugin [analysis-seunjeon]

기존 플러그인이 없다고 했었는데 위 처럼 변경 됩니다.

 

복병은 따로 있었네요 ^^;

 

SeunjeonTokenizerFactory.java:25에서 없는 클래스를 찾나봐요 ㅠㅠ

 

찾아보니 6.1.4까지만 해도 있던 놈이..ㅠㅠ

 

6.2.0에 흔적도 없이 사라짐..ㅠㅠ

 

아 이럴수가 ㅠ_ㅠ....

ES 6.1.4로 다시 설치해야겠죠?!

 

> wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.4.tar.gz
> tar -xvf elasticsearch-6.1.4.tar.gz
> bash <(curl -s https://bitbucket.org/eunjeon/seunjeon/raw/master/elasticsearch/scripts/downloader.sh) -e 6.1.4 -p 6.1.1.1
> bin/elasticsearch-plugin install file://`pwd`/elasticsearch-analysis-seunjeon-6.1.1.1.zip

 

그리고 테스트 스크립트를 돌려보면~!

아주 잘 작동 되는걸 확인 할 수 있습니다.ㅠ_ㅠ

 

삽질 아닌 삽질을 하며, 여기까지 왔네요.

도움 되셨으면 좋겠어용~~~

 

오늘도 도움주신 Elastic Stack 관계자 및 은전한닢 그리고 한글형태소 분석기를 위해 애쓰는분들 감사드립니다.

 

추가글)

실행과 종료에 대한 간단한 스크립트 추가 하도록 할게요!

이전 버전 elasticsearch에 사용되던 스크립트인데, 책에서 참고한 내용 입니다.

> echo 'bin/elasticsearch -d -p es.pid' > start.sh
> echo 'kill `cat es.pid`' > stop.sh
> chmod 755 start.sh stop.sh
# 위키북스 시작하세요 엘라스틱서치! 에서 발췌

+ Recent posts