서론: 간단히 배경
회사에서 2021년 하반기부터 검색 쪽을 맡아서 개발하고 있는데 정작 Elasticsearch(이하 ES)의 내부 구조를 전혀 모르니 읽어봐야겠다.
이 글을 적는 시점의 가장 최신 버전인 8.2가 대상이다.
본론
소스: https://github.com/elastic/elasticsearch
$ ./gradlew localDistro
Gradle이란
Groovy나 Kotlin DSL을 사용하는 빌드시스템이다. Kotlin DSL이 가독성이 더 좋고 컴파일 시간을 체크하거나 IDE와 호환이 되기 때문에 많이들 선호하는 편이다.
JVM에서 실행되기 때문에 Java, Kotlin, Scala 등 JVM 계열 언어로 만들어진 모듈을 빌드할 때 사용한다.build.gradle
파일이 있는 폴더를 하나의 프로젝트로 인식한다. build.gradle
파일에는 플러그인 등 설정 내용 등을 기재한다. 그 외에는 빌드, 어플리케이션 실행, 테스트 실행, 디플로이 등 개발에 필요한 태스크를 실행하는 스크립트를 적는다.
Maven처럼 프로젝트에서 사용하는 라이브러리를 build.gradle
파일에 정의해 종속된 라이브러리까지 함께 다운로드한다.
Gradle을 사용하기 위해서는 JDK 8이상을 사용해야한다. 아래 커맨드로 현재 사용중인 JDK 버전을 확인할 수 있다.
$ java -version
나는 맥북을 사용하니 Homebrew를 이용한다. 아래 커맨드로 Gradle을 설치할 수 있다.
$ brew install gradle
$ gradle -v
프로젝트 폴더 최상단에서 아래 커맨드를 실행해 Gradle을 사용할 준비를 한다.
$ gradle init
<Gradle Wrapper란>
Gradle을 적절한 버전으로 실행할 때 Gradle Wrapper(이하 래퍼)를 사용하면 좋다고 한다. 적절한 버전을 사용해주는 것 외에도 사전에 필요한 것들을 알아서 준비하기 때문에 편리하다.
위 커맨드로 아래 것들이 생성된다:
- 래퍼를 사용하기 위한 파일
- gradle/wrapper/ (래퍼 파일이 놓이는 폴더)
- gradle-wrapper.jar
- gradle-wrapper.properties
- gradlew (래퍼를 사용하기 위한 스크립트)
- gradlew.bat (래퍼를 사용하기 위한 스크립트)
- gradle/wrapper/ (래퍼 파일이 놓이는 폴더)
- 설정 파일
- settings.gradle.kts (빌드와 서브프로젝트 이름을 정의하는 파일)
- buildSrc/build.gradle.kts (의존관계를 정의하는 파일)
- buildSrc/src/main/kotlin/ (Groovy나 Kotlin DSL로 구현한 컨벤션 플러그인을 보존하는 폴더)
- 서브 프로젝트를 빌드하기 위한 스크립트
- app/build.gradle.kts
- list/build.gradle.kts
- utilities/build.gradle.kts
- 소스가 들어있는 폴더
- app/src/main/
- list/src/main/
- utilities/src/main/
- 테스트를 넣는 폴더
- app/src/test
- list/src/test
ES에서 사용하는 Gradle
Github에는 아래 파일이 있었다.
- build.gradle
- gradle.properties
- gradlew (shell스크립트)
- gradlew.bat (shell스크립트)
- settings.gradle
- gradle/
- wrapper/
- gradle-wrapper.jar (Gradle 디스트리뷰젼을 다운로드하기 위한 코드가 들은 래퍼 JAR파일)
- gradle-wrapper.properties (Gradle버전 등 래퍼 실행을 위한 속성값 등을 적은 파일)
- build.versions.toml
- verification-metadata.xml
- wrapper/
위에 적었다시피(ES의 readme에 나와있듯이) gradlew
파일을 실행해서 빌드를 시작한다.
# 리마인드
$ ./gradlew localDistro
app_path
는 ./gradlew
이다.
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
daisy-chained symlinks
라는 것은 참조의 참조의 참조의... 참조하는 파일과 같이 연속적으로 연결(daisy chain)되어있는 심볼릭 링크(소프트 링크; 특정 파일이나 디렉토리를 지정하는 별도의 파일을 작성해 본체를 참조할 수 있도록 하는 것)를 말하는 것 같다. 만약 실행한 파일이 심볼릭 파일일 때 실제 스크립트 파일의 패스를 취득하고자 하는 것이 아래 코드의 의도인 것 같다.
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
이렇게 된 김에 shell스크립트 공부도 하고자 하나하나 뜯어보겠다.
quitta블로그를 보면 %
%%
는 패턴에 일치하는 부분을 가장 오른쪽에서부터 제거해 필요한 부분문자열만 얻는 방법이고 #
##
는 패턴에 일치하는 부분을 가장 왼쪽에서부터 제거해 필요한 부분문자열만 얻는 방법이라고 한다.
따라서 아래 ${app_path##*/}
는 파일이름만을 얻기 위한 처리이니 gradlew
만이 남을것이다.APP_HOME
은 gradlew
부분을 제거한 디렉토리부분이 될테니 ./
이다.
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[...]
는 test
커맨드의 단축형이고 -h
옵션은 해당 패스가 존재하는 심볼릭 링크인지 확인하기 위한 것이다. 심볼릭 링크가 존재하면 True를 존재하지 않으면 False를 반환한다.
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
ls
커맨드는 파일이나 디렉토리의 정보를 리스트로 돌려주는데 -ld
옵션을 붙이면 해당 파일이 보존되어있는 디렉토리의 상세 정보를 출력한다. -l
이 상세정보를 반환하기 위한 옵션이고 -d
가 디렉토리의 정보를 받기 위한 옵션이다.
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
->
부분을 확인하기 위해 심볼릭 링크를 만들어 실행해봤다. link
가 /*
패턴에 맞지 않아 아래 설정으로는 app_path
가 ././test
가 된다. 아마 보통은 이렇지 않겠지.
$ vim ./test
$ ln -s ./test ./test.symbolic
$ ls -ld ./test.symbolic
lrwxr-xr-x 1 {user_dir} staff 12 Aug 17 18:17 ./test.symbolic -> ./test
# link => ./test
오늘은 여기까지..
Gradle과 shell구문 찾느라 분량에 비해 꽤나 시간을 썼다.