mikl

mikl

I have a multi-step Gitlab CI pipeline, where I’m trying to avoid downloading Hex dependencies multiple times, but I’m struggling with the step where the Docker image is built.

In previous steps, I set MIX_HOME to a _mix folder inside the build structure, to be able to cache it with Gitlab CI cache and to pass it and the _build and deps folders around for use by separate lint and test steps. That works fine, but when I try to re-use said folders for the step that builds a Docker image, it doesn’t work as expected. The relevant parts of my Dockerfile:

FROM hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612-slim AS build

# install build dependencies
RUN apt update -y && apt install -y build-essential curl git

# prepare build dir
WORKDIR /app

# set build ENV
ENV MIX_ENV=prod
ENV MIX_HOME=/app/_mix

# copy Mix dependencies from previous step
COPY _build _mix deps mix.exs mix.lock /app/
COPY config config

# install hex + rebar
RUN mix local.hex --force && \
  mix local.rebar --force

RUN mix deps.compile

COPY priv priv
COPY assets assets
COPY lib lib
RUN mix compile

However, despite copying both _build, deps and the MIX_HOME folder, when Docker reaches the mix compile step here, it errors:

Unchecked dependencies for environment prod:
* telemetry_metrics (Hex package)
  the dependency is not available, run "mix deps.get"
* phoenix_live_view (Hex package)
  the dependency is not available, run "mix deps.get"
[and so on for every package]

Shouldn’t it be finding the packages, since I’ve copied over the deps folder?

I’ve also tried running RUN mix compile --no-deps-check instead, but that just throws different errors like module Ecto.Query is not loaded and could not be found.. So Elixir is truly unable to find said modules.

It seems that copying the aforementioned folders into the Docker environment should work, so what am I missing here?

Showing Posts 1 to 8

sergio-ocon

sergio-ocon

Have you tried more advanced Dockerfile syntax?

Slides 13 an on,
RUN --mount=type=cache

D4no0

D4no0

This line is a bit suspicious:

COPY _build _mix deps mix.exs mix.lock /app/

Are you using the gitlab’s CI cache? And if yes, where this cache is populated in the first place?

I would suggest to drop the _build cache if you compiled these files anywhere else before, because I also had this problem, I ended up dropping cache on “docker in docker” builds since I had some cryptic compile errors.

mikl

mikl OP

That might be faster in this case, but is there any reason why it shouldn’t work with plain old copy?

mikl

mikl OP

Yeah, I have a previous step looking like this:

.marsvin:
  image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612-slim
  variables:
    MIX_ENV: "test"
    # To have hex and rebar included in cache/artifacts, put MIX_HOME inside
    # the build root.
    MIX_HOME: "${CI_PROJECT_DIR}/packages/marsvin/_mix"

marsvin_mix_install:
  extends: .marsvin
  stage: init
  needs: []
  dependencies: []
  cache:
    key: "marsvin_mix_install"
    paths:
      - packages/marsvin/_mix
      - packages/marsvin/_build
      - packages/marsvin/deps
  before_script:
    - cd packages/marsvin
    - mix local.hex --force --if-missing
    - mix local.rebar --force --if-missing
  script:
    - mix deps.get
    - mix deps.clean --unused
    - mix compile --warnings-as-errors
  artifacts:
    paths:
      - packages/marsvin/_mix
      - packages/marsvin/_build
      - packages/marsvin/deps
    expire_in: 7 days

The exported artifacts are then used in the build step, like this:

marsvin_build:
  extends: .marsvin
  stage: build
  needs:
    - "marsvin_mix_install"
    - "marsvin_mix_test"
  dependencies: ["marsvin_mix_install"]
  image: ${BUILDKIT_IMAGE}
  variables:
    APP_VERSION: "${APP_VERSION}"
    IMAGE_URL_INTERNAL: "${DOCKER_REGISTRY_INTERNAL}/${DOCKER_NS_INT}/marsvin"
  script:
    - cd packages/marsvin
    - ${BUILDKIT_SCRIPT}
      --tag=${IMAGE_URL_INTERNAL}:${IMAGE_VERSION}
      --cache-repo=${IMAGE_URL_INTERNAL}:cache
      --context=.
      --file=Dockerfile

Yeah, I only added that to try to solve aforementioned problem, does not appear to make a difference anyway.

sergio-ocon

sergio-ocon

This solves the external cache problem, but I don’t know why it is failing. Are you using the same user to run your container?

Why don’t you use a release for this? Then you can have a multistage Dockerfile, and keep the running container lean (you don’t need Elixir there, just the libraries and the minimal requirements needed to run the release. Actually, mix phx.gen.release uses that approach and works really well (an the final image is smaller and does not contain anything not needed so it is more secure and faster to copy)

mikl

mikl OP

True, it might be (much) easier to simply build the release outside of the Docker-in-Docker environment, and reduce the Dockerfile to just copy that and call it done. That neatly sidesteps the dependency problem. I’ll try that instead.

Edit: Building the release outside the Dockerfile works great. Thanks for the idea.

w0rd-driven

w0rd-driven

It’s probably not worth mentioning now but your cache key being static would mean every build that uses marsvin_mix_install would try to share the same cache. That’s every commit on every branch.

I use the variable $CI_COMMIT_REF_NAME which creates a cache for each branch. A good visual primer is A visual guide to GitLab CI/CD caching to understand the various strategies. A single key may be fine if you don’t branch at all since I think later commits drop earlier jobs.

You can also do print debugging by adding RUN ls -al cache/directory to output contents in the logs. Its hacky as hell and there may be better approaches like setting up a runner locally. You could also do stuff like that in the .gitlab-ci.yml script or before script sections.

spaceCowboy

spaceCowboy

Just reviewing your .gitlab-ci.yml. In marsvin_build you have both needs: and dependencies: . This might work just fine, but Gitlab recommends not having both in the same layer: CI/CD YAML syntax reference | GitLab Docs, as one relies on current stage, and the other relies on previous build stage. IDK if that changes anything, but thanks for sharing your scripts.

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews