|
@ -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. |