::: IT인터넷 :::

Docker로 Scala 빌드머신 만들기 (2) - Dockerfile 폴더 구성

곰탱이푸우 2023. 8. 14. 08:20
Scala 개발환경 구성이 완료되면 작성한 Application을 빌드하고 배포해야 한다.
이전에 Jenkins로 Python과 Docker 빌드/배포 환경을 구축한 방법을 활용하여 Scala Application의 빌드 머신을 생성한다.

 

Jenkins의 SSH Agent 컨테이너의 Dockerfile과 sbt를 활용하여 리눅스 기반의 Scala 빌드 에이전트를 생성한다.
생성한 빌드 에이전트는 SSH 통신을 이용하여 Jenkins에 에이전트로 등록한다.
 
진행 순서는 다음과 같다.
  • Jenkins SSH 에이전트 이미지 생성 Dockerfile 작성
  • Dockerfile 빌드에 필요한 외부 파일 다운로드와 폴더 구성
  • Jenkins 에이전트 이미지 빌드와 배포
 
이전 글에서 Dockerfile을 작성했으므로 Docker 이미지 빌드를 위한 폴더 구성을 진행한다.
Dockerfile을 작성하는 방법은 아래 글을 참고한다.
Docker 빌드 머신 생성 방법은 아래 글을 참고한다.
Dockerfile을 작성하여 Docker로 빌드할 것이므로 Docker가 설치되어 있어야 한다.
 
 

전체 파일 구성

Scala 빌드 머신의 전체 파일 구조는 크게 세 부분으로 구성된다.
  • Dockerfile - Docker 이미지 생성 스크립트
  • Jenkins 연동을 위한 에이전트
  • 빌드머신 구성에 필요한 환경 설정
 
빌드머신 구성에 필요한 환경 설정 파일들은 다음과 같이 구성된다.
  • apt 설정 - 우분투 apt 패키지 설정
  • sbt 설정 - sbt 실행에 필요한 설정
  • ssh 설정 - Jenkins 연동을 위한 ssh 관련 파일
  • 설치 파일 - 빌드 머신에 설치할 deb 파일
  • 테스트 파일 - 기본 라이브러리 다운로드를 위한 임시 build.sbt 파일
 
위와 같은 기준으로 정리한 전체 파일 구조는 다음과 같다.
각 항목별 설명은 주석을 참고한다.
\data
    \apt_conf\etc\apt
        \sources.list    # apt 저장소 설정
    \sbt_conf
        \etc\sbt
            \sbtopts    # sbt 설정 (저장소, 인증서 관련)
        \home\jenkins\
            \.sbt
                \credentials    # 배포 저장소 인증 정보
                \credentials.sbt    # 배포 저장소 인증 설정
                \nexus-url    # 배포할 sbt 저장소 목록
                \repositories    # sbt 저장소 목록
            \.ssh
                \authorized_keys    # 인증 정보 저장 파일
            \certs    # 자체서명인증서인 경우
                \output.cert    # 사설 저장소 서버 인증서
                \rootca.crt    # 사설 저장소 서버 rootca 인증서
            \test
                \build.sbt    # 기본 라이브러리 로드를 위한 build.sbt 예제
    \sbt_install\home\jenkins
        \sbt-1.9.0.deb    # sbt 1.9.0 설치 파일
Dockerfile    # Docker 이미지 빌드를 위한 Dockerfile
setup-sshd    # Jenkins Agent 연동을 위한 setup-sshd
version    # Docker 이미지 버전과 배포 정보
 
 

version 파일 작성

해당 Dockerfile을 Docker 이미지로 빌드하고 배포하기 위한 정보를 정의한다.
REPO_PATH=Docker저장소주소:포트 # Docker 로컬 저장소 주소와 포트
IMAGE_NAME=sr-scala-agent    # 생성할 Docker 이미지의 이름
IMAGE_VERSION=1.0.0.dev    # 생성할 Docker 이미지의 버전
 
위의 내용들은 Jenkins의 Docker 빌드 파이프라인에서 Inject environment variables 플러그인을 통해 환경 변수로 전달된다.
관련 내용은 아래 문서의 Build (빌드) 항목을 참고한다.

apt의 sources.list 파일 작성

해당 Docker 컨테이너에서 사용할 apt의 저장소 목록을 변경한다.
 
인터넷 환경에서 기본 apt 저장소를 사용하는 경우에는 생략해도 된다.
기본 apt 저장소 주소는 아래와 같다.
 
apt 저장소를 사설 저장소 (또는 미러 저장소)로 변경하고자 하는 경우 변경한다.
sources.list 관련 내용은 아래 문서를 참고한다.
 

sbt 설정 파일 작성

Spark Application을 sbt로 빌드하고 배포하기 위해 필요한 파일들을 작성한다.
폴더 구성에는 구분이 없지만, 아래 정리는 빌드와 배포로 구분하여 정리한다.
 

빌드 관련 파일

sbt를 이용하여 Spark Application을 빌드하기 위해 필요한 설정 파일을 작성한다.
빌드되면 jar 포맷의 바이너리가 생성된다.
 

sbtopts 작성

sbt의 설정을 관리하는 sbtopts 파일을 작성한다.
실제 파일 경로는 /etc/sbt/sbtopts 이다.
 
설정한 내용들은 sbt가 실행되면서 실행 인자로 전달된다.
 
sbtopts 파일 내용은 다음과 같다. 자세한 내용은 주석을 참고한다.
# build.sbt가 없으면 sbt 기본 프로젝트 구조 생성
-sbt-create
# 사용자가 지정한 sbt 저장소를 바라보도록 설정
-Dsbt.override.build.repos=true
# sbt가 사용하는 Heap 메모리 크기 설정 (단위는 MB)
-mem 1024
# sbt가 사용하는 JVM의 Heap 메모리 크기 설정
-J-Xmx2G
# 배포하려는 저장소의 인증서가 자체서명 인증서가 아닌 경우 아래 내용은 생략한다.
# 자체서명 인증서의 검증 생략 옵션 지정
-Dmaven.wagon.http.ssl.insecure=true
-Dmaven.wagon.http.ssl.allowall=true
-Dmaven.wagon.http.ssl.ignore.validity.dates=true
-Djavax.net.ssl.trustStore=$JAVA_HOME/jre/lib/security/cacerts
-Djavax.net.ssl.trustStorePassword=changeit
 
 

repositories 작성

앞서 sbtopts에 지정한 환경 설정에 다음과 같은 옵션을 지정했다.
# /etc/sbt/sbtopts
-Dsbt.override.build.repos=true
 
sbt가 사용하는 라이브러리 저장소 목록에 사용자가 지정한 저장소 목록을 사용하겠다는 의미이다.
 
라이브러리 저장소는 의존성이 있는 다른 라이브러리를 받아오기 위해 사용한다.
따라서 sbt가 사용할 라이브러리 저장소 목록을 작성한다.
실제 파일 경로는 /home/jenkins/.sbt/repositories 이다.
 
repositories 파일 내용은 다음과 같다. 줄바꿈 부분은 한줄로 연결한다.
[repositories]
local
ivy-remote-scalasbt: https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/,
                    [organization]/[module]/(scala_[scalaVersion]/)
                    (sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
ivy-remote-sbt-ivy: https://scala.jfrog.io/artifactory/ivy-releases,
                    [organization]/[module]/(scala_[scalaVersion]/)
                    (sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
ivy-remote-typesafe-ivy: https://scala.jfrog.io/ui/native/ivy-releases/,
                        [organization]/[module]/(scala_[scalaVersion]/)
                        (sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
maven-public: https://저장소주소:포트/repository/maven-public/
maven-release: https://저장소주소:포트/repository/maven-releases/
maven-snapshot: https://저장소주소:포트/repository/maven-snapshots/
 
세부 내용은 다음과 같다. 작성 순서를 탐색 우선 순위로 이해해도 된다.
  • local - 로컬에 저장된 라이브러리들을 우선 사용한다.
  • ivy-* : sbt는 기본적으로 ivy 저장소를 사용한다. (remote-sbt, remote-repos, remote-jetbrain)
  • maven-* : sbt는 maven 저장소도 사용할 수 있다. (remote-repos, remote-repos-ai, public, snapshot, release)
 
maven 저장소 목록을 추가하고자 하는 경우 아래 문서를 참고한다.

build.sbt 작성

sbt 기본 라이브러리와 spark 관련 라이브러리를 다운로드하기 위해 필요한 build.sbt 파일을 작성한다.
build.sbt가 복사 될 경로는 /home/jenkins/test/build.sbt 이다.
 
build.sbt 파일 내용은 다음과 같다. 자세한 내용은 주석을 참고한다.
import Keys._

val sparkVersion = "3.2.3"    // Spark 버전
val hadoopVersion = "3.3.1"    // Hadoop 버전

lazy val root = (project in file("."))
    .settings(
        name := "sparkexample",    // Application 이름
        version := "0.1.0.1-SNAPSHOT",    // Application 버전
        versionScheme := Some("semver-spec"),    // Semantic 버전 체계 사용
        organization := "com.bearpooh.test",    // 소속 조직 정보 (FQDN의 역방향)
        scalaVersion := "2.12.18",    // 사용하는 Scala 버전
        libraryDependencies ++= Seq(    // 의존성 있는 라이브러리 지정
            "org.apache.spark" %% "spark-core" % sparkVersion,    // spark-core 라이브러리
            "org.apache.spark" %% "spark-sql" % sparkVersion,    // spark-sql 라이브러리
            "org.apache.hadoop" % "hadoop-client" % hadoopVersion    // hadoop-client 라이브러리
        ),
        publishMavenStyle := true    // Maven 저장소에 배포하려는 경우
    )

 

sbt는 build.sbt 파일이 존재하면 scalaVersion과 libraryDependencies에 명시 된 라이브러리들을 다운로드한다.
sbt update 명령을 수행하면 관련 라이브러리들을 다운로드할 수 있다.
 
기본 라이브러리들을 빌드할 때마다 다운로드하면 시간도 오래 걸리며 비효율적이다. 
Spark, Scala, sbt 버전은 고정된 상태이므로 기본 라이브러리를 미리 다운로드하여 빌드 시간을 단축한다.

 

 

배포 관련 파일

빌드 과정을 통해 jar 파일이 생성되었다면 저장소로 배포해야 한다.
저장소에 배포 된 jar 파일은 이후 Livy를 통해 Spark 클러스터에 전달한다.
 
배포 관련 기능들을 sbt 플러그인으로 제작하는 방법도 가능하다.
그러나 배포 관련 사항은 빌드 머신에서 스크립트로 처리하도록 설정한다.
Spark Application은  필요 기능만 제작할 수 있고 배포 관련 사항은 고려하지 않아도 된다.
 

nexus-url 작성

생성 된 jar 파일을 업로드할 maven 저장소 주소를 SNAPSHOTS와 RELEASES로 구분하여 지정한다.
build.sbt의 버전 정보에 SNAPSHOT 유무에 따라 snapshots 저장소와 releases 저장소로 구분하여 배포할 것이기 때문이다.
 
실제 파일 경로는 /home/jenkins/.sbt/nexus-url 이다.
nexus-url 파일 내용은 다음과 같다.
# Inject environment variables로 환경 변수 등록
OCEAN_NEXUS=https://NexusIP주소:포트/repository
OCEAN_SNAPSHOTS=${OCEAN_NEXUS}/maven-snapshots
OCEAN_RELEASES=${OCEAN_NEXUS}/maven-releases
 
위의 내용은 Jenkins의 빌드 파이프라인에서 Inject environment variables 플러그인을 통해 환경 변수로 등록하여 사용한다.
 
Nexus에 Maven 저장소를 설정하는 방법은 아래 문서를 참고한다.

credentials 작성

Nexus에 생성한 Maven 저장소에 접근하기 위한 계정 정보 파일을 작성한다.
일반적으로 빌드에 사용할 계정을 미리 생성해두고, 해당 계정 정보를 사용하면 된다.
 
실제 파일 경로는 /home/jenkins/.sbt/credentials 이다.
credentials 파일 내용은 다음과 같다.
realm=Sonatype Nexus Repository Manager
host=NexusIP주소
user=please_modify
password=please_modify
 
realm은 Sonatype의 Nexus 저장소인 경우 Sonatype Nexus Repository Manager로 지정한다.
host에는 FQDN 또는 IP 주소를 입력한다. 포트 번호는 생략해야 한다.
 
Dockerfile의 계정 정보는 "수정 요망" 메시지를 남겨둔다.
소스코드의 형상 관리를 하는 경우 하드코딩으로 남겨둔 계정 정보는 유출의 위험이 높다.
해당 Docker 이미지를 컨테이너로 생성하고, 컨테이너 내부에 진입해서 계정 정보를 수정한다. (아주 중요하다!!)
 
relam과 host 설정 관련 내용은 아래 문서를 참고한다.
 

credentials.sbt 작성

저장소 인증 정보를 build.sbt 파일에 전달하기 위한 credentials.sbt 파일을 작성한다.
해당 파일은 build.sbt와 동일한 폴더에 존재해야 한다.
 
모든 Spark Application 프로젝트마다 해당 파일을 생성하는 것은 비효율적이다.
동일한 내용이기 때문에 빌드머신의 .sbt 폴더에 저장해두고, 빌드할 때마다 build.sbt 파일의 경로에 복사해서 사용한다.
해당 작업은 빌드 스크립트에서 설정하면 된다.
 
실제 파일 경로는 /home/jenkins/.sbt/credentials.sbt 이다.
credentials.sbt 파일 내용은 다음과 같다. 자세한 내용은 주석을 참고한다.
import sbt._
import java.io.File

// .sbt/credentials 파일의 경로
val credentialsFilePath: String = System.getProperty("user.home") + "/.sbt/credentials"
// .sbt/credentials을 읽고 Seq[Credentials] 객체로 변환
val nexusCredentials: Seq[Credentials] = {
  // .sbt/credentials 파일을 읽어옴
  val credentialsFile = new File(credentialsFilePath)
  // 읽어들인 계정 정보를 Credentials 객체에 저장
  Credentials(credentialsFile) :: Nil
}
// sbt의 credentials에 nexusCredentials 정보 추가
credentials ++= nexusCredentials
 
sbt pulish 명령이 수행되면 sbt는 credentials.sbt 파일을 실행하여 Nexus 저장소 계정 정보를 읽고 배포한다.