Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a developer be able to create a docker artifact from a lerna monorepo in their development environment?

I've recently started using lerna to manage a monorepo, and in development it works fine.

Lerna creates symlinks between my various packages, and so tools like 'tsc --watch' or nodemon work fine for detecting changes in the other packages.

But I've run into a problem with creating docker images in this environment.

Let's say we have a project with this structure:

root
  packages
     common → artifact is a private npm package, this depends on utilities, something-specific
     utilities → artifact is a public npm package
     something-specific -> artifact is a public npm package
     frontend → artifact is a docker image, depends on common
     backend → artifact is a docker image, depends on common and utilities

In this scenario, in development, everything is fine. I'm running some kind of live reload server and the symlinks work such that the dependencies are working.

Now let's say I want to create a docker image from backend.

I'll walk through some scenarios:

  1. I ADD package.json in my Dockerfile, and then run npm install.

    Doesn't work, as the common and utilities packages are not published.

  2. I run my build command in backend, ADD /build and /node_modules in the docker file.

    Doesn't work, as my built backend has require('common') and require('utilities') commands, these are in node_modules (symlinked), but Docker will just ignore these symlinked folders.

    Workaround: using cp --dereference to 'unsymlink' the node modules works. See this AskUbuntu question.

  3. Step 1, but before I build my docker image, I publish the npm packages.

    This works ok, but for someone who is checking out the code base, and making a modification to common or utilities, it's not going to work, as they don't have privledges to publish the npm package.

  4. I configure the build command of backend to not treat common or utilities as an external, and common to not treat something-specific as an external.

    I think first build something-specific, and then common, and then utilities, and then backend.

    This way, when the build is occuring, and using this technique with webpack, the bundle will include all of the code from something-specfic, common and utilities.

    But this is cumbersome to manage.

It seems like quite a simple problem I'm trying to solve here. The code that is currently working on my machine, I want to pull out and put into a docker container.

Remember the key thing we want to achieve here, is for someone to be able to check out the code base, modify any of the packages, and then build a docker image, all from their development environment.

Is there an obvious lerna technique that I'm missing here, or otherwise a devops frame of reference I can use to think about solving this problem?

like image 802
dwjohnston Avatar asked May 24 '19 07:05

dwjohnston


People also ask

What is Monorepo lerna?

Introduction. Lerna is a tool for managing JavaScript projects with multiple packages. Lerna manages monorepos, which can hold projects containing multiple packages within itself. Monorepos can be challenging to manage because sequential builds and publishing individual packages take a long time.

Why do we need lerna?

Lerna allows teams to build libraries and apps in a single repository called a monorepo. Since we don't have to publish to NPM until we're ready to go, it's faster to iterate locally when building components that depend on each other.

Where do I put my Dockerfiles in Monorepo?

Step 2: Add dev Dockerfiles to the project Vs-Code adds all that is required such as dockerignore files and even debug configuration if specified. The frontend Dockerfile is located in the frontend/web folder. The backend Dockerfile is located in backend folder.


1 Answers

We got a similar issue and here is what we did: Put the Dockerfile in the root of the monorepo (where the lerna.json locates).

The reason: You really treat the whole repo as a single source of truth, and you want any modification to the whole repo to be reflected in the docker image, so it makes less sense to have separate Dockerfiles for individual packages.

Dockerfile

FROM node:12.13.0

SHELL ["/bin/bash", "-c"]

RUN mkdir -p /app
WORKDIR /app

# Install app dependencies
COPY package.json /app/package.json
COPY yarn.lock /app/yarn.lock
COPY packages/frontend/package.json /app/packages/frontend/package.json
COPY packages/backend/package.json /app/packages/backend/package.json
COPY lerna.json /app/lerna.json
RUN ["/bin/bash", "-c", "yarn install"]

# Bundle app source
COPY . /app
RUN ["/bin/bash", "-c", "yarn bootstrap"]
RUN ["/bin/bash", "-c", "yarn build"]

EXPOSE 3000

CMD [ "yarn", "start" ]

package.json

{
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "bootstrap": "lerna clean --yes && lerna bootstrap",
    "build": "lerna run build --stream",
    "start": "cross-env NODE_ENV=production node dist/backend/main",
  },
  "devDependencies": {
    "lerna": "^3.19.0",
    "cross-env": "^6.0.3"
  },
}

like image 136
Felix Avatar answered Sep 17 '22 14:09

Felix