Using Docker for Scala

In this guide, we will learn the process of setting up a simple Docker container for use with Scala. We will start by creating a simple Dockerfile to set up the environment and use it to build a Docker image. This image can be used to run a Docker container that allows us to compile Scala projects and even play around with the language in an interactive shell. All of this without muddying up our system or conflicting with other installations that we might have.

What is Docker?

While this guide’s focus is not on the core concepts or use cases of Docker, it’s good to have a basic idea about what it is before we dig in. Docker gives us a nice way to prepare an environment to handle a particular task without being invasive to our main operating system. It is important that we not confuse Docker and containers with a hypervisor and virtual machines, but the analogy may be useful in building an initial understanding.

According to docker.com:

Docker containers wrap a piece of software in a complete filesystem that contains everything needed to run: code, runtime, system tools, system libraries – anything that can be installed on a server. This guarantees that the software will always run the same, regardless of its environment.

This guide will display a casual convenience that Docker can provide to a developer, but its power goes far beyond this example. This guide only scratches the surface. For a much deeper explanation of Docker and its power, check out the Docker Deep Dive course offered by Linux Academy.

What is Scala?

Scala is a general-purpose programming language [with] full support for functional programming and a strong static type system. Designed to be concise, many of Scala’s design decisions were inspired by criticism of Java’s shortcomings.

via Wikipedia

Let’s begin

Before we get started, you need to have Docker installed and have the daemon running (i.e. having the app running). Run the docker -v command from a Terminal window. If you see version information for Docker, then we are ready to go!

We will cover the following steps:
  1. Gather Resources: Scala requires Java, so we need to prepare our image with both.
  2. Prepare our Dockerfile: A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
  3. Build an image: An image is essentially a list of commands used to prepare the environment.
  4. Push the image to Docker Hub: The Docker Hub is an online repository for Docker images.
  5. Learn to use our Scala container: A container is an instance of an image. Our “Scala container” would then be an instance of the image we create in this guide.

If you care to push your image to the Docker Hub later on, go ahead and sign up and remember your username.

1. Gathering resources

Our goal is to create a Docker image that has been prepared to use Scala. We can then use this image to create containers (instances of an image) that let us use Scala. This allows us to avoid cluttering our operating system with installation files, environment variables, etc. Let’s start out by preparing a directory for us to work in.

Open a terminal window and create/navigate to a fresh directory (referred to as ‘working directory’ from here on). For example:

mkdir ~/docker-scala
cd ~/docker-scala

Let’s put the files we need (Scala and Java) in your working directory. You may do this any way you please, either by command line or GUI if you have it. We will be using an Ubuntu base image for our container, so choose accordingly:

Once you’ve downloaded both, verify that the two files are in your working directory. For the sake of this guide, we will assume they are named jdk1.8.0_111.tar.gz and scala-2.12.0-M1.tgz.

2. Preparing the Dockerfile

For our purposes here, you can think of a Dockerfile as a text file that lists how we want to prepare our image. You can read more about Dockerfiles, their keywords, and their formatting in the Docker documentation or in the Docker Deep Dive course on Linux Academy.

Our Dockerfile will be very simplistic and only consist of a few sections:

  • Define the base image that we want to use. We will use Ubuntu in this example.
  • Define path variables for the Scala and Java installations.
  • Move the Scala and Java files over.
  • Provide a CMD to clean up usage

We need an empty file called Dockerfile in our working directory. You can create this from the command line using touch Dockerfile (be sure you have navigated to the folder first). Open this file up in your favorite text editor.

We use the FROM keyword to define the base image that we want to start with. Again, we will be using the ubuntu image, but you can use any image you like. If Docker doesn’t see image locally, it will seek it out from Docker Hub.

So let’s define our base image in the Dockerfile:

# Define the environment
FROM ubuntu

You will often see images in the form “image:tag,” but if you don’t explicitly define a tag, then Docker assumes latest. Comments are denoted with a #.

Now we can set up the required PATH variables:

To ensure Scala and Java are in working order, we can tell the Dockerfile to set some environment variables for us. This is done with the ENV keyword:

ENV variableName value

Let’s add this to our our Dockerfile:

# Define environment variables
ENV SHARE /usr/local/share
ENV SCALA_HOME $SHARE/scala
ENV JAVA_HOME $SHARE/java
ENV PATH=$SCALA_HOME/bin:$JAVA_HOME/bin:$PATH

It’s time to move over those downloaded files:

The ADD keyword in a Dockerfile lets Docker know that we need to move something from our local filesystem to the image. It’s important to notice that ADD will also unpackage these archives automatically, which is nice in our case!

ADD localDir remoteDir

Note: Remember that the files I downloaded to my working directory earlier were called jdk1.8.0_111.tar.gz and scala-2.12.0-M1.tgz. Yours are likely different, so just use the name of your files here.

Add this to the Dockerfile:

# Move over JDK and Scala
ADD jdk1.8.0_111.tar.gz /
ADD scala-2.12.0-M1.tgz /

This will unpackage and copy over the contents. At this point, the image would have two folders in the root directoy: /jdk1.8.0_111 and /scala-2.12.0-M1. Now we want to use the mv command from Ubuntu to put them in the desired location. We can use the RUNkeyword in the Dockerfile to indicate a command that we want to on the image as if it were running.

Since we defined the $SCALA_HOME and $JAVA_HOME varilables earlier, we can use them here. Let’s add this to our Dockerfile:

# Get JDK and Scala into place
RUN mv /jdk1.8.0_111 $JAVA_HOME
RUN mv /scala-2.12.0-M1 $SCALA_HOME

Lastly, we want to use the CMD keyword to help things act a little cleaner when we interact with our container later on. Add this final line to the Dockerfile:

CMD ["/bin/bash"]

At this point, our simple Dockerfile should be completed. Here’s a summary of the entire file to make sure everything is correct.

Summary of Dockerfile‘s contents:

# Define the environment
FROM ubuntu

# Define environment variables
ENV SHARE /usr/local/share
ENV SCALA_HOME $SHARE/scala
ENV JAVA_HOME $SHARE/java
ENV PATH=$SCALA_HOME/bin:$JAVA_HOME/bin:$PATH

# Move over JDK and Scala
ADD jdk1.8.0_111.tar.gz /
ADD scala-2.12.0-M1.tgz /

# Get JDK and Scala into place
RUN mv /jdk1.8.0_111 $JAVA_HOME
RUN mv /scala-2.12.0-M1 $SCALA_HOME

CMD ["/bin/bash"]
3. Building the image

In the previous step, we told Docker how we wanted our image to be set up. Now it’s time to let it do its thing. We can use Docker’s build functionality in terminal to prepare an image from this Dockerfile.

docker build <options> path

One <option> that we will use here is -t for tagging. My Docker Hub account name is davisengeler and I want to tag this image as docker-scala, so I want to use the tag davisengeler/docker-scala. Think of a proper tag for your image; we are about to create one.

In terminal, make sure you have navigated to the directory with our downloads and Dockerfile (example: cd ~/docker-scala). Now we can build the image with:

docker build -t davisengeler/docker-scala .

Note: Be sure to replace davisengeler/docker-scala with your own tag. The . at the end indicates the current directory in terminal.

Docker now downloads any necessary resources from Docker Hub, starts a container from the base image, sets the variables for Scala and Java, extracts their contents, and puts them into place, then creates an image out of the container’s final state.

Troubleshooting: You may run into an issue with the RUN mv ... section. If the build fails because one of the folders doesn’t exist, try this:

  1. Add RUN ls to your Dockerfile just above the RUN mv... commands. Save the file, return to terminal, and run the build command again.
  2. You will still get the error, but you should see a list of files (bin, boot, dev, etc…). Within this list, there will be two listings that relate to Scala and Java. Take note of their exact names.
  3. Modify the two lines in your Dockerfile that start with RUN mv ... so the folder name matchs their respective listings you took note of in troubleshooting step above.
  4. Run the build command in Terminal again. If the build succeeded, I suggest removing the RUN ls line from your Dockerfile and calling the build command just one more time.
4. Pushing image to Docker Hub

If all went as planned, you should see Successfully built and an ID. You can run docker images to make sure you see your new image in the list (you can identify it by the tag you created). If you care to publicly push this image up to your Docker Hub account, you can do so with Docker’s push command in terminal. For example:

docker push davisengeler/docker-scala

Docker will push up the image as efficiently as possible. Anything used in an existing Docker Hub image will just be linked to your new one instead of re-uploading everything. Nice!

Now you should be able to see this image listed on your Docker Hub account page.

5. Using our creation

So we’ve created an image for Scala and uploaded it to Docker Hub. Now it’s time to get some use out of the thing! Remember the image name you used when creating it. Mine was davisengeler/docker-scala.

Interactive Scala Shell

We can use this container to play with Scala’s interactive shell. It’s a handy tool for trying out code or learning to use Scala. To get to it, we will use Docker’s run command with the -itoptions to make it interative. Be sure to replace my image name with your own.

docker run -it davisengeler/docker-scala

This will start a container and put us in a bash prompt. Here we can run the scala to enter the interactive shell. Try it out with print("Hey, Scala!"). Use the :quit command to leave the Scala shell, then use “exit” to return to your main terminal prompt.

Compiling / Running a Scala Project

We can also use this container to compile and run Scala projects. For a very simple example, let’s create a very simple Hello World in Scala. Create a local file /scala_example/hello.scala with the contents:

object Hello extends App {
println("Hello, world")
}

We can compile and run this program with Scala on our container. Let’s start a container with our local /scala_example/ directory mounted as a volume (we can use the -v SRC:DEST command when we run the container):

docker run -it -v /scala_example:/scala_example davisengeler/docker-scala

Note: The -it option tells the container to run interactively. Don’t forget to change my image name to your own.

We are presented with a bash prompt inside the container with the /scala_exampledirectory mounted. Let’s navigate to that directory, compile the program, and run it using scalac and scala:

cd /scala_example
scalac hello.scala
scala hello

Hello, world

You can exit the prompt with the exit command.

Comments are disabled for this guide.