sbt 1.2.xのライブラリ依存性解決が遅いんじゃ!


はじめに

Apache Sparkフレームワークを使用してビックデータ処理をScalaで記述しています。
処理をデプロイするためのjarファイルをGitLab CI/CDで作成しています。
しかし、毎回ライブラリ依存性解決が遅すぎる!
そのときの解決策です。

環境

項目 バージョン
バージョン管理システム GitLab
GitLab CI/CD Runner Docker Executor
Scala 2.11
sbt 1.2

なぜ依存関係の解決が遅いのか

sbt 1.2までは依存関係の解決にApache Ivyをしており、これが原因らしいです。
sbt 1.3以降はCoursierがデフォルトで使用されるようになり、改善されています。

Dockerイメージ

GitLab CI/CDのRunnerとしてDocker Executorを使用しているので、Dockerイメージを作成が必要です。
公式ドキュメントの記載されている通り、そのDockerイメージにCoursierをグローバルプラグインとして追加します。

Dockerfile

scala-sbtを参考に作成しています!

Dockerfile
#
# Scala and sbt Dockerfile
#
# https://github.com/hseeberger/scala-sbt
#

# Pull base image
ARG BASE_IMAGE_TAG
FROM openjdk:${BASE_IMAGE_TAG:-8u312-jdk-bullseye}

# Env variables
ARG SCALA_VERSION
ENV SCALA_VERSION ${SCALA_VERSION:-2.13.8}
ARG SBT_VERSION
ENV SBT_VERSION ${SBT_VERSION:-1.6.2}
ARG COURSIER_VERSION
ENV COURSIER_VERSION ${COURSIER_VERSION:-2.0.0-RC6-8}
ARG USER_ID
ENV USER_ID ${USER_ID:-1001}
ARG GROUP_ID 
ENV GROUP_ID ${GROUP_ID:-1001}

# Install sbt
RUN \
  curl -fsL "https://github.com/sbt/sbt/releases/download/v$SBT_VERSION/sbt-$SBT_VERSION.tgz" | tar xfz - -C /usr/share && \
  chown -R root:root /usr/share/sbt && \
  chmod -R 755 /usr/share/sbt && \
  ln -s /usr/share/sbt/bin/sbt /usr/local/bin/sbt

# Install Scala
RUN \
  case $SCALA_VERSION in \
    "3"*) URL=https://github.com/lampepfl/dotty/releases/download/$SCALA_VERSION/scala3-$SCALA_VERSION.tar.gz SCALA_DIR=/usr/share/scala3-$SCALA_VERSION ;; \
    *) URL=https://downloads.typesafe.com/scala/$SCALA_VERSION/scala-$SCALA_VERSION.tgz SCALA_DIR=/usr/share/scala-$SCALA_VERSION ;; \
  esac && \
  curl -fsL $URL | tar xfz - -C /usr/share && \
  mv $SCALA_DIR /usr/share/scala && \
  chown -R root:root /usr/share/scala && \
  chmod -R 755 /usr/share/scala && \
  ln -s /usr/share/scala/bin/* /usr/local/bin && \
  case $SCALA_VERSION in \
    "3"*) echo "@main def main = println(util.Properties.versionMsg)" > test.scala ;; \
    *) echo "println(util.Properties.versionMsg)" > test.scala ;; \
  esac && \
  scala -nocompdaemon test.scala && rm test.scala

# Install rpm for sbt-native-packager
# see https://github.com/hseeberger/scala-sbt/pull/114
RUN apt-get update && \
  apt-get install rpm bc -y && \
  rm -rf /var/lib/apt/lists/*

# Add and use user sbtuser
RUN groupadd --gid $GROUP_ID sbtuser && useradd -m --gid $GROUP_ID --uid $USER_ID sbtuser --shell /bin/bash
USER sbtuser

# Switch working directory
WORKDIR /home/sbtuser

# Prepare sbt (warm cache)
RUN \
  sbt sbtVersion && \
  mkdir -p project && \
  echo "scalaVersion := \"${SCALA_VERSION}\"" > build.sbt && \
  echo "sbt.version=${SBT_VERSION}" > project/build.properties && \
  echo "// force sbt compiler-bridge download" > project/Dependencies.scala && \
  echo "case object Temp" > Temp.scala && \
  if [ "$(echo "$(echo ${SBT_VERSION} | cut -c 1-3) < 1.3" | bc)" -eq 1 ]; then \
    mkdir -p .sbt/1.0/plugins; \
    echo "addSbtPlugin(\"io.get-coursier\" % \"sbt-coursier\" % \"${COURSIER_VERSION}\")" > .sbt/1.0/plugins/build.sbt \
  fi && \
  sbt compile && \
  rm -r project && rm build.sbt && rm Temp.scala && rm -r target

# Link everything into root as well
# This allows users of this container to choose, whether they want to run the container as sbtuser (non-root) or as root
USER root
RUN \
  ln -s /home/sbtuser/.cache /root/.cache && \
  ln -s /home/sbtuser/.sbt /root/.sbt && \
  if [ -d "/home/sbtuser/.ivy2" ]; then ln -s /home/sbtuser/.ivy2 /root/.ivy2; fi

# Switch working directory back to root
## Users wanting to use this container as non-root should combine the two following arguments
## -u sbtuser
## -w /home/sbtuser
WORKDIR /root  

CMD sbt

使い方

sbt 1.2.8, scala 2.11.12のイメージを作成する場合は下記のコマンドを実行します。

docker build --build-arg SBT_VERSION="1.2.8" --build-arg SCALA_VERSION="2.13.1" .

変更点

  • 変数の追加

Dockerイメージのビルドで使用する引数とデフォルトの値を設定します。

Dockerfile(一部抜粋)
ARG COURSIER_VERSION
ENV COURSIER_VERSION ${COURSIER_VERSION:-2.0.0-RC6-8}
  • 小数を比較用のパッケージをインストール

バージョンの比較を行うためにbcをインストールします。

Dockerfile(一部抜粋)
  apt-get install rpm bc -y && \
  • sbt-coursierをグローバルプラグインに追加

sbtのバージョンが1.3未満を条件にグローバルプラグインとしてsbt-coursierを設定します。

Dockerfile(一部抜粋)
  if [ "$(echo "$(echo ${SBT_VERSION} | cut -c 1-3) < 1.3" | bc)" -eq 1 ]; then \
    mkdir -p .sbt/1.0/plugins; \
    echo "addSbtPlugin(\"io.get-coursier\" % \"sbt-coursier\" % \"${COURSIER_VERSION}\")" > .sbt/1.0/plugins/build.sbt \
  fi && \

終わりに

依存解決に30分以上要していましたが、Coursierを追加することで5分で終わるようになりました!
そもそもはsbtをバージョンアップしろって話ですよね。。。