If you’re interested in making a commercial grade Clojure web app that deploys as a Docker container, this is the tutorial for you.
Prerequisites: You will need Leiningen and Docker installed.
First step needed is to create a Clojure web app template project. I used the following command.
1 |
lein new luminus mywebapp +reitit +http-kit +mysql +swagger +auth |
This creates a Clojure web app using the Luminus Framework, Reitit for mapping of incoming requests to the proper handlers, HTTP Kit for HTTP handling, MySQL for JDBC connections to our MySQL database, Swagger for API support, and Buddy for authentication middleware. Your new web app is in the directory, mywebapp .
Before starting your web app, your database connection needs to be configured in dev-config.edn . Something similar to the following should be added to that *.edn file.
1 |
:database-url "mysql://yourIPaddress:3306/mywebapp_dev?user=tdavis&password=db_user_password_here" |
At this point it is important to note that you will run into all kinds of problems with Docker if you try to use localhost (or 127.0.0.1) with docker for your database connection. Instead, configure it to use the IP address of your computer, and configure your MySQL user to accept connections from everywhere. This simulates using a remote MySQL server.
nrepl is another consideration when creating Docker images for Clojure apps, including our web app. If you want to expose a remote REPL from inside the docker container, you will need to specify an :nrepl-port and an :nrepl-bind address. I recommend adding the following configuration lines to your *.edn config file.
1 2 |
:nrepl-port 7000 :nrepl-bind "0.0.0.0" |
To start up the web app cd to the mywebapp directory and enter lein run . You should then be able to navigate to http://localhost:3000/ from your web browser. When you’re done playing with the new web app, use control-c to stop the server.
Okay! You have a working web app. Now let’s containerize it.
We need an uber jar when building our docker container. In turn, to build the docker container, we need to embed the prod configuration file. Configure your env/prod/resources/config.edn to match your dev-config.edn
Now create an uberjar for mywebapp. An uberjar is a giant JAR file containing every dependency for mywebapp. Run the following command to create the uberjar.
1 |
lein uberjar |
We’re ready for the docker build, … almost. First we need to get our Dockerfile configured for our build. Here’s my Dockerfile .
1 2 3 4 5 6 7 8 9 |
FROM adoptopenjdk:11-jre-hotspot RUN mkdir /opt/app COPY target/uberjar/mywebapp.jar /mywebapp/app.jar EXPOSE 3000 EXPOSE 7000 CMD ["java", "-jar", "/mywebapp/app.jar"] |
This Dockerfile uses Java 11, and exposes port 3000 for the webserver and port 7000 for the remote REPL connections.
Next, we’ll check that our initial Dockerfile is building.
1 |
docker build . |
As an alternative build, use the -t for naming/tagging the docker image you’re building.
1 |
docker build -t myapp-v1 . |
Finally, run the docker container locally with the following command.
1 |
docker run --rm --name mywebappcontainer -p 3000:3000 -p 7000:7000 -it <image ID> |
The image ID comes from the docker build. Look for a line similar to the following in your output.
=> => writing image sha256:0c826b218048658976b05f82a8160fa7c61aaedb769307d78b93a34d13b46479
The part after sha256: is your image ID.
Just for completeness, let’s go over the rest of the docker run options used above.
-it allows interactive processes like shells.
--rm removes containers when they are shutdown. This is desirable when testing, but probably not wanted in a production environment.
--name gives a friendly name to the container started by docker run .
-p exposes a port of the app running in the docker container to the the rest of the network.
In a production environment, you’re likely to want to run docker containers in detached mode. Use -d to run your container in the background.
There you go. A complete Dockerized Clojure web app. Once you work through this tutorial, you should have enough information to create your own Docker web apps with Clojure. Enjoy!