Fork me on GitHub
#polylith
<
2024-04-29
>
ag02:04:37

Question about building a project. I got all the bricks - base, components, etc. I now want to dockerize it. Where do I place the Dockerfile? I suppose it belongs inside the project folder. Okay. Now another dilemma, where do I build the project? Should my Dockerfile build script copy entirety of the repo source and build it within, or should it expect the pre-built uberjar to exists, so it can simply just use it? On one hand, building inside the docker insures a clean, repeatable build. On the other hand it will take longer to download all dependencies and if deps change frequently Docker won't cache the layers. How do you folks containerize your Polylith projects?

seancorfield03:04:44

We just build uberjars, and do everything through build.clj in the top of the repo...

seancorfield03:04:45

...but I would put the Dockerfile in projects/<whatever> and have the "build" for it build the uberjar first and then the image -- and make that part of build.clj at the top-level.

seancorfield03:04:03

The "build" for each project could be completely different and have its own unique process -- but we prefer to encapsulate all of that in one build.clj at the root -- although having a build.clj for each project is also perfectly reasonable, as is having a custom build script in each project.

ag03:04:53

Interesting. I'm contemplating of building inside Dockerfile - calling clojure -T:build from within. And setting up .dockerignore to ignore everything except bases/ components/ projects/ and root deps.edn. However, I think this would force me to keep Dockerfile outside of /projects/project folder, because Docker doesn't recognize the build context beyond the directory where Dockerfile sits.

seancorfield03:04:49

That would force all of the :build dependencies to end up in the Docker image tho', right? Or am I misunderstanding what you mean by "from within"?

ag03:04:41

Yes, basically copy the source, then build, then delete everything leaving only the .jar.

ag03:04:06

That would make it very isolated and ensures that the project can be built on any machine and CI. But some drawback are there too

ag03:04:19

It will have to fetch the dependencies. Every time. And will be slow. Maybe it's not a good idea after all.

ag03:04:44

Oh, it actually worked well. I didn't even have to move the Dockerfile. I have created .dockerignore in the root:

*
!bases/
!components/
!projects/
!deps.edn
Dockerfile
FROM clojure AS clj_container
RUN mkdir /app
COPY . /app
WORKDIR /app/projects/shitty-api
RUN clojure -T:build uber

RUN mv /app/projects/shitty-api/target/shitty-api.jar /
RUN rm -rf /app

FROM amazoncorretto
COPY --from=clj_container /shitty-api.jar /

EXPOSE 3003
CMD ["java", "-jar", "/shitty-api.jar"]
And then built it from the root: docker build -f ./projects/shitty-api/Dockerfile . -t shitty

seancorfield04:04:13

Nice! Does it even need the workspace-level deps.edn file? I assume :build is in projects/shitty-api/deps.edn and build.clj is in that folder too?

ag05:04:04

Oh, so the root deps.edn is not even used to build projects? Ah... alright.

seancorfield05:04:38

Well, we use it to build projects because we have build.clj in root and it can build any/all projects 🙂 so our workspace deps.edn has our :build alias 🙂

ag05:04:22

Yes, that makes sense. Why would one write a separate build script for each project? In my case, it's a single project and as you have probably guessed, not of the greatest quality. I kid of course. I changed the name in the snippet above. The real project named differently

seancorfield06:04:05

Our Polylith repo has almost two dozen projects (and two dozen bases but they aren't quite 1:1), and over 160 components and our build.clj file only builds uber JAR files for projects that have changed since the last CI pass (which tags the repo). Cuts down on build time if only one or two projects changed since the last run.

👀 1
ag06:04:56

Very impressive

Patrix09:04:05

My polylith repo only has 2 projects, and this is how I've structured: • build.clj in the projects/blabla directory to build the uberjar • Dockerfile in the projects/blabla directory to build the Docker image • whenever I deploy (I haven't implemented any automated pipeline yet for this, it's coming soon and will follow the same process), the work takes place within the projects/blabla directory, therefore no .dockerignore etc needed (although it might help optimize the build I suppose); just copy the target jar file and other resources in the image, with paths relative to projects/blabla • deployment (using kamal) also uses the same folder for its configs and runs from that folder essentially what projects are about in polylith IIUC 🙂 I'm not building from inside a container, since ultimately the pipeline will run in a container (e.g. AWS CodeBuild) or whatever GitHub Actions uses.. Kindof curious about a global build.clj, that might be better in the long run before I start duplicating so much build.clj code in each project...

seancorfield16:04:35

The Polylith project itself takes the "global build.clj" approach -- which in turn is modeled on what we did at World Singles Networks (since I contributed a lot of it). https://github.com/polyfy/polylith/blob/master/build.clj

👍 1
seancorfield16:04:43

Polylith is a bit weird since it builds a library as well as uberjars, but the principle is similar.

Patrix01:04:21

Ahh thanks, I’ll check out their build.clj

Patrix03:04:20

ok that was painless enough (haven't implemented the deployment via build.clj, just the build.. which is good enough for now).