Requirements
The first requirement, which is very important, is adding.moon/cache to the workspace root .dockerignore (moon assumes builds are running from the root). Not all files in .moon/cache are portable across machines/environments, so copying these file into Docker will definitely cause interoperability issues.
.dockerignore
git commands under the hood, there are some special considerations to be aware of when running moon within Docker. There’s 2 scenarios to choose from:
- (recommended) Add the
.gitfolder to.dockerignore, so that it’s notCOPY’d. moon will continue to work just fine, albeit with some functionality disabled, like caching. - Ensure that the
gitlibrary is installed in the container, and copy the.gitfolder withCOPY. moon will work with full functionality, but it will increase the overall size of the image because of caching.
Creating a Dockerfile
Our
moon docker file command can automatically generate a Dockerfile based on this guide! We suggest generating the file then reading the guide below to understand what’s going on.Dockerfiles are to write and maintain, so in an effort to reduce this headache, we’ve built a handful of tools to make this process much easier. With moon, we’ll take advantage of Docker’s layer caching and staged builds as much as possible.
With that being said, there’s many approaches you can utilize, depending on your workflow:
- Running
moon dockercommands before runningdocker run|buildcommands. - Running
moon dockercommands within theDockerfile. - Using multi-staged or non-staged (standard) builds.
- Something else unique to your setup!
What we’re trying to avoid
Before we dive into writing a perfectDockerfile, we’ll briefly talk about the pain points we’re trying to avoid. In the context of Node.js and monorepo’s, you may be familiar with having to COPY each individual package.json in the monorepo before installing node_modules, to effectively use layer caching. This is very brittle, as each new application or package is created, every Dockerfile in the monorepo will need to be modified to account for this new package.json.
Furthermore, we’ll have to follow a similar process for only copying source files necessary for the build or CMD to complete. This is very tedious, so most developers simply use COPY . . and forget about it. Copying the entire monorepo is costly, especially as it grows.
Scaffolding the bare minimum
The first step in this process is to only copy the bare minimum of files necessary for installing dependencies (Node.js modules, etc). This is typically manifests (package.json), lockfiles (yarn.lock, etc), and any configuration (.yarnrc.yml, etc).
This can all be achieved with the moon docker scaffold command, which scaffolds a skeleton of the repository structure, with only necessary files. Let’s create our Dockerfile:
Copy source files
The next step is to copy all source files necessary for
CMD or any RUN commands to execute correctly. The moon docker scaffold <project> command has already done this for us!If you need to copy additional files for your commands to run successfully, you can configure the
docker.scaffold settings in .moon/workspace.yml (entire workspace) or moon.* (per project).Prune extraneous files
Now that we’ve ran a command or built an artifact, we should prune the Docker environment to remove unneeded files and folders. We can do this with the When ran, this command will do the following, in order:
moon docker prune command.- Remove extraneous dependencies (
node_modules) for unfocused projects. - Install production only dependencies for the projects that were scaffolded.
This process can be customized using the
docker.prune setting in .moon/workspace.yml.Final result
And with this moon integration, we’ve reduced the originalDockerfile significantly! The original file can be seen as O(n), as each new manifest requires cascading updates, while the moon approach is O(1)!
Running docker commands
When runningdocker commands, they must be ran from moon’s workspace root (typically the repository root) so that the project graph and all moon docker commands resolve correctly.
Dockerfiles are located within each applicable project, use the -f argument.
Troubleshooting
Supporting node:alpine images
If you’re trying to use thenode:alpine image with moon’s integrated toolchain, you’ll need to set the MOON_TOOLCHAIN_FORCE_GLOBALS environment variable in the Docker image to disable moon’s toolchain. This is required as Node.js does not provide pre-built binaries for the Alpine target, so installing the Node.js toolchain will fail.