Personal blog written from scratch using Node.js, Bootstrap, and MySQL. https://jrtechs.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
5.0 KiB

  1. Alrighty, folks, this blog post is pretty straightforward from the title.
  2. We are going to be running [Scala](https://scala-lang.org/) code in [Docker](https://www.docker.com/) containers.
  3. Specifically, we will be using SBT and docker-compose.
  4. SBT is a built tool primarily used by Scala developers, and docker-compose is a tool for defining docker environments.
  5. To start, we need to create a simple Docker container that can build our scala code.
  6. From an existing Java JDK container, SBT is straightforward to install from a package manager.
  7. ```bash
  8. FROM openjdk:8u232
  9. ARG SBT_VERSION=1.4.1
  10. # Install sbt
  11. RUN \
  12. mkdir /working/ && \
  13. cd /working/ && \
  14. curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \
  15. dpkg -i sbt-$SBT_VERSION.deb && \
  16. rm sbt-$SBT_VERSION.deb && \
  17. apt-get update && \
  18. apt-get install sbt && \
  19. cd && \
  20. rm -r /working/ && \
  21. sbt sbtVersion
  22. RUN mkdir -p /root/build/project
  23. ADD build.sbt /root/build/
  24. ADD ./project/plugins.sbt /root/build/project
  25. RUN cd /root/build && sbt compile
  26. EXPOSE 9000
  27. WORKDIR /root/build
  28. CMD sbt compile run
  29. ```
  30. There are a few things to note about this docker file.
  31. First, we are only adding the two SBT files and then running a simple SBT compile command when we build the container.
  32. This SBT compile command only used to pull in general dependencies so that the end Docker container can launch faster.
  33. 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.
  34. For reference, I include the two SBT files I'm using in this project:
  35. build.sbt:
  36. ```bash
  37. name := """alert-api"""
  38. organization := "net.jrtechs"
  39. version := "1.0-SNAPSHOT"
  40. lazy val root = (project in file(".")).enablePlugins(PlayScala)
  41. scalaVersion := "2.13.2"
  42. resolvers += Resolver.JCenterRepository
  43. libraryDependencies += guice
  44. libraryDependencies += "net.katsstuff" %% "ackcord" % "0.16.1" //For high level API, includes all the other modules
  45. libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test
  46. libraryDependencies += "org.mongodb.scala" %% "mongo-scala-driver" % "2.9.0"
  47. ```
  48. plugins.sbt:
  49. ```bash
  50. addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.1")
  51. addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0")
  52. ```
  53. Now that we have our docker file, we can create our docker-compose script to launch the application.
  54. For this application, I am attaching the container to a simple bridged network with a port exposed.
  55. ```yaml
  56. version: '3.1'
  57. networks:
  58. external-network:
  59. external:
  60. name: external-network
  61. services:
  62. sbt:
  63. build:
  64. context: ./
  65. dockerfile: ./docker/Dockerfile
  66. image: sbt
  67. ports:
  68. - "9000:9000"
  69. volumes:
  70. - "./:/root/build"
  71. networks:
  72. - external-network
  73. ```
  74. 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.
  75. To create the docker network, we need to issue this command -- it only needs to be run once.
  76. ```bash
  77. docker network create -d bridge external-network
  78. ```
  79. To run the project, we can use the docker-compose up command:
  80. ```bash
  81. docker-compose run
  82. ```
  83. 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.
  84. To properly forward the required ports when using docker-compose run, you need to pass in the "--service-ports" flag.
  85. ```bash
  86. docker-compose run --service-ports sbt /bin/bash
  87. sbt
  88. compile
  89. run
  90. ```
  91. 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.
  92. ```bash
  93. **
  94. !docker
  95. !build.sbt
  96. !project/plugins.sbt
  97. ```
  98. That is it. I enjoy the docker approach towards developing Scala applications since it keeps the environment consistent across machines.
  99. 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.
  100. I only need to install a text editor and Docker to develop projects with vastly different build environments and configurations.
  101. All the complexity with setting up the environment can get relegated to the Docker container.
  102. 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.