Docker setup for REPL-driven development

My normal setup works very nicely, using CIDER in emacs for both front-end and back-end (C-M-J). Things get complicated as I try to get my student workers set up, especially if there’s a database involved; for this reason, Docker would make a lot of sense. But my docker-fu is a bit weak; I don’t expect I’ll be able to get anything as easy as C-M-J, but how might I set up a compose situation that includes my DB (e.g. postgres) and also will provide me all the REPL hot-loading glory I need for development? Does anyone have any docker-based workflows or sample DOCKERFILE they would share?

1 Like

I don’t have a Dockerfile ready to hand (or a docker-compose.yml for that matter) but I think that if your goal is to have a Docker image where e.g., the JDK and lein live, but still be able to have REPL-driven development with CIDER against the Clojure runtime inside that container, it would be as simple as:

  • Mount your local project dir as a volume inside the container
  • EXPOSE an nREPL port (and do the necessary config in your project to specify a known port for nREPL)
  • start the project up with something like cd /mounted/project/volume && lein repl as your image ENTRYPOINT
  • use cider-connect instead of cider-jack-in to attach to the REPL
  • visit the files you want to change on your local disk, C-M-J to your heart’s content

In this case you’re running the REPL inside the container, connecting to it “remotely” from your emacs process on the host machine, while visiting/editing files that are on local disk but also mounted into the container.

2 Likes

I’ve also found this relevant video, although he jumps through all kinds of hoops and it’s a bit imposing for those of us with time constraints… https://www.youtube.com/watch?v=FtkHgQSSb3c

1 Like

Most of what @chris mentioned - minor differences mostly for development:

  • The host’s ~/.m2 folder is also mounted inside the container.
  • Instead of EXPOSE'ing ports and configuring the project, I use --net=host on the docker run command.
  • WORKDIR instead of cd within ENTRYPOINT
  • ENTRYPOINT is just lein. This is so that you are able to easily run commands other than repl at container start (you can use repl as default through CMD). Most of the time, though, ENTRYPOINT is set to a shell for more flexibility.
2 Likes

Ah – an excellent blog post that covers the setup with clojure in detail: http://danlebrero.com/2017/09/25/how-do-docker-compose-development-environement/ . I’ll be experimenting with this and will report back.

I don’t have a template Dockerfile extracted, but the rough steps I followed to get CIDER attached (to a remote container, which is slightly different from your needs) were:

  1. expose a REPL port from the container to the outside world
  2. embed an nREPL server in your application, so the app and the REPL are in the same process
  3. on your local machine, have CIDER 0.16.0 installed and {:user {:plugins [[cider/cider-nrepl "0.16.0"]]}} in your lein profile (~/.lein/profiles.clj)
  4. M-x cider-connect / C-c C-c

I put the host in my SSH config (~/.ssh/config) and therefore need to have the variable nrepl-use-ssh-fallback-for-remote-hosts set to t, which you can do by putting (setq nrepl-use-ssh-fallback-for-remote-hosts t) in your ~/.emacs.d/init.el.

This process makes the Docker side of things rather minimal—just the port.

1 Like

Did you get an answer to this? I have a component-based app, where one of the components is a repl-server. But the main point is that i) your repl in your container, will be listening on a port. And ii) you just need to expose that port, outside of the container.

So with an app Dockerfile as in A), you can have a docker-compose setup, as in B). Once you i) docker build ... your image then ii) docker-compose up, you can connect to the repl on the localhost port 5678. Hth.

A) Dockerfile

FROM pandeiro/lein:latest

COPY project.clj /your-app-location
WORKDIR /your-app-location
RUN lein deps

B) docker-compose.yml

version: '3.3'
services:

  some-db:
    image: namespace/some-db:1.2.3
    ports:
      - 1234:1234

  your-app:
    image: namespace/your-app:latest
    depends_on:
      - some-db
    ports:
      - 5678:5678
    entrypoint: lein some-lein-alias-that-runs-a-repl-on-port-5678
1 Like