diff --git a/blogContent/posts/java/running-scala-code-in-docker.md b/blogContent/posts/java/running-scala-code-in-docker.md new file mode 100644 index 0000000..74bd314 --- /dev/null +++ b/blogContent/posts/java/running-scala-code-in-docker.md @@ -0,0 +1,140 @@ + + + +Alrighty, folks, this blog post is pretty straightforward from the title. +We are going to be running [Scala](https://scala-lang.org/) code in [Docker](https://www.docker.com/) containers. +Specifically, we will be using SBT and docker-compose. +SBT is a built tool primarily used by Scala developers, and docker-compose is a tool for defining docker environments. + +To start, we need to create a simple Docker container that can build our scala code. +From an existing Java JDK container, SBT is straightforward to install from a package manager. + +```bash +FROM openjdk:8u232 + +ARG SBT_VERSION=1.4.1 + +# Install sbt +RUN \ + mkdir /working/ && \ + cd /working/ && \ + curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \ + dpkg -i sbt-$SBT_VERSION.deb && \ + rm sbt-$SBT_VERSION.deb && \ + apt-get update && \ + apt-get install sbt && \ + cd && \ + rm -r /working/ && \ + sbt sbtVersion + +RUN mkdir -p /root/build/project +ADD build.sbt /root/build/ +ADD ./project/plugins.sbt /root/build/project +RUN cd /root/build && sbt compile + +EXPOSE 9000 +WORKDIR /root/build + +CMD sbt compile run +``` + +There are a few things to note about this docker file. +First, we are only adding the two SBT files and then running a simple SBT compile command when we build the container. +This SBT compile command only used to pull in general dependencies so that the end Docker container can launch faster. +Second, notice that we are exposing port 9000; this port is only for the web application I am building. Finally, note that /root/build will be the root directory for the Scala SBT application. + + +For reference, I include the two SBT files I'm using in this project: + +build.sbt: +```bash +name := """alert-api""" +organization := "net.jrtechs" + +version := "1.0-SNAPSHOT" + +lazy val root = (project in file(".")).enablePlugins(PlayScala) + +scalaVersion := "2.13.2" + +resolvers += Resolver.JCenterRepository + +libraryDependencies += guice + +libraryDependencies += "net.katsstuff" %% "ackcord" % "0.16.1" //For high level API, includes all the other modules + +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test + +libraryDependencies += "org.mongodb.scala" %% "mongo-scala-driver" % "2.9.0" +``` + +plugins.sbt: +```bash +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.1") +addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0") +``` + +Now that we have our docker file, we can create our docker-compose script to launch the application. +For this application, I am attaching the container to a simple bridged network with a port exposed. + +```yaml +version: '3.1' + +networks: + external-network: + external: + name: external-network + +services: + + sbt: + build: + context: ./ + dockerfile: ./docker/Dockerfile + image: sbt + ports: + - "9000:9000" + volumes: + - "./:/root/build" + networks: + - external-network +``` + +The main thing to note about the docker-compose script is that I placed our Dockerfile in a separate docker directory. Additionally, I'm mounting the scala project directory into the container as /root/build. The volume enables us to edit the project on our local machine while the Docker container compiles our code. + +To create the docker network, we need to issue this command -- it only needs to be run once. +```bash +docker network create -d bridge external-network +``` + +To run the project, we can use the docker-compose up command: + +```bash +docker-compose run +``` + +To use the SBT shell, we need to open a terminal to the running container. After we have the shell, we can issue all of our standard SBT commands. +To properly forward the required ports when using docker-compose run, you need to pass in the "--service-ports" flag. + +```bash +docker-compose run --service-ports sbt /bin/bash +sbt +compile +run +``` + +Since the location where we launch docker-compose is in the same directory as all of our scala code and build artifacts, we must create a ".dockerignore" file. Otherwise, Docker will scan the entire directory before building the container -- causing massive frustration. The "**" in the Docker ignore file tells Docker to ignore everything, and the "!" tells Docker to include that file. Alternatively, we could have just excluded the target build directory. + +```bash +** +!docker +!build.sbt +!project/plugins.sbt +``` + +That is it. I enjoy the docker approach towards developing Scala applications since it keeps the environment consistent across machines. +As someone who enjoys distro-hopping, it is nice not having to figure out how to install Java, SBT, Scala, and countless other development environments on every operating system I use. +I only need to install a text editor and Docker to develop projects with vastly different build environments and configurations. +All the complexity with setting up the environment can get relegated to the Docker container. + +This approach went over a container geared towards Scala development. For production, I would recommend that you use this SBT image to build a fat JAR, and then copy it into a lightweight JRE container using Docker's multi-stage build functionality.