Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: BuildArgs in custom docker builds #241

Closed
lorenzomigliorero opened this issue Jul 21, 2024 · 13 comments · Fixed by #249
Closed

Feat: BuildArgs in custom docker builds #241

lorenzomigliorero opened this issue Jul 21, 2024 · 13 comments · Fixed by #249
Labels
enhancement New feature or request

Comments

@lorenzomigliorero
Copy link
Contributor

lorenzomigliorero commented Jul 21, 2024

What problem will this feature address?

Currently, there's no way to specify build-args for custom dockerfile projects.
Example use-case: In case of a private npm registry, there's no way to declare an NPM_TOKEN build variable.

Describe the solution you'd like

I see three possible ways:

  • Create another table under Environment Settings called Build Args
  • Create another top-level menu item called Build Args close to Environment
  • Specify which environments are also build-args

This is just pseudo-code, but the buildImage command should accept a buildargs parameter:

server/utils/builders/docker-file.ts

import type { WriteStream } from "node:fs";
import { docker } from "@/server/constants";
import * as tar from "tar-fs";
import type { ApplicationNested } from ".";
import { getBuildAppDirectory } from "../filesystem/directory";
import { createEnvFile } from "./utils";

export const buildCustomDocker = async (
	application: ApplicationNested,
	writeStream: WriteStream,
) => {
++	const { appName, buildargs, env } = application;
	const dockerFilePath = getBuildAppDirectory(application);
	try {
		const image = `${appName}`;
		const contextPath =
			dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || ".";
		const tarStream = tar.pack(contextPath);

		createEnvFile(dockerFilePath, env);

		const stream = await docker.buildImage(tarStream, {
			t: image,
++			buildargs,
			dockerfile: dockerFilePath.substring(dockerFilePath.lastIndexOf("/") + 1),
		});

		await new Promise((resolve, reject) => {
			docker.modem.followProgress(
				stream,
				(err, res) => (err ? reject(err) : resolve(res)),
				(event) => {
					if (event.stream) {
						writeStream.write(event.stream);
					}
				},
			);
		});
	} catch (error) {
		throw error;
	}
};

Dockerode issue: apocas/dockerode#326
Engine API docs: https://docs.docker.com/engine/api/v1.24/#build-image-from-a-dockerfile

Describe alternatives you've considered

There are no other alternatives since the buildargs argument is the only param available.

Additional context

No response

@lorenzomigliorero lorenzomigliorero added the enhancement New feature or request label Jul 21, 2024
@Siumauricio
Copy link
Contributor

Siumauricio commented Jul 21, 2024

hi, @lorenzomigliorero I recently add support for enviroment variables in build time for Dockerfiles (Not released yet), is not args but it makes almost the same purpose to have variables in build time #227

@lorenzomigliorero
Copy link
Contributor Author

Oh cool! I didn't see that issue. Is there a way to test the feature?

@Siumauricio
Copy link
Contributor

Siumauricio commented Jul 21, 2024

Yes, you can install the canary version curl -sSL https://dokploy.com/canary.sh | sh I don't recommend since it can introduce some bugs or something but if you want to test it out would be awesome!

@lorenzomigliorero
Copy link
Contributor Author

I ran the canary branch locally, but variables are unavailable at build time.

@lorenzomigliorero
Copy link
Contributor Author

lorenzomigliorero commented Jul 21, 2024

@Siumauricio , I did a quick test, and I can confirm that the only way is to use the buildArgs flag. Creating the env file during the DockerFile creation isn't enough.

const stream = await docker.buildImage(tarStream, {
	t: image,
	buildargs: {
		foo: "bar" // this works
	},
	dockerfile: dockerFilePath.substring(dockerFilePath.lastIndexOf("/") + 1),
});

The easiest may be converting the env variable into a JSON: Eg: buildargs: convertEnvToJSON(env), however the Docker Engine API says: This is not meant for passing secret values.

So, I suggest creating another field or flagging which variables are also build-time (as Coolify does).

@Siumauricio
Copy link
Contributor

@lorenzomigliorero can you share your dockerfile?

@lorenzomigliorero
Copy link
Contributor Author

Sure, the NPM_TOKEN is needed as ARG for the yarn --production cmd.

FROM node:12-alpine as build

ARG NPM_TOKEN
ARG NODE_ENV=production

RUN env

WORKDIR /opt/
COPY package.json yarn.lock .npmrc ./
# Strapi requirements
RUN apk add --no-cache python2 make g++ vips-dev
RUN yarn --production

WORKDIR /opt/app
COPY . .
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn run build

FROM node:12-alpine

RUN apk add --no-cache vips-dev

ARG NPM_TOKEN
ENV NPM_TOKEN=$NPM_TOKEN

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /opt/
COPY --from=build /opt/node_modules ./node_modules

ENV PATH /opt/node_modules/.bin:$PATH

WORKDIR /opt/app
COPY --from=build /opt/app .

EXPOSE 1337
CMD ["yarn", "start"]

@Siumauricio
Copy link
Contributor

Siumauricio commented Jul 21, 2024

You have 2 options

  1. Remove ARG and adapt to the ENV structure
  2. Remove the ARG and ENV, and just assign the ENV values in the Enviroment Tab and it automatically will create a .env file which the dockerfile will pick, something like this..
FROM node:12-alpine as build

RUN env

WORKDIR /opt/
COPY package.json yarn.lock .npmrc ./
# Strapi requirements
RUN apk add --no-cache python2 make g++ vips-dev
RUN yarn --production

WORKDIR /opt/app
COPY . .
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn run build

FROM node:12-alpine

RUN apk add --no-cache vips-dev

WORKDIR /opt/
COPY --from=build /opt/node_modules ./node_modules

ENV PATH /opt/node_modules/.bin:$PATH

WORKDIR /opt/app
COPY --from=build /opt/app .

EXPOSE 1337
CMD ["yarn", "start"]

@lorenzomigliorero
Copy link
Contributor Author

lorenzomigliorero commented Jul 21, 2024

Both options can't work (removing the ARG FOO command won't solve).
Here is an example:

Environments

FOO=100
BAR=200
LOREM=300

Dockerfile

FROM node:12-alpine as build
ARG FOO
RUN env

Without buildArgs

NODE_VERSION=12.22.12
HOSTNAME=dbab621bb9ec
YARN_VERSION=1.22.18
SHLVL=1
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/

With buildargs

const stream = await docker.buildImage(tarStream, {
	t: image,
	buildargs: {
		FOO: "bar"
	},
	dockerfile: dockerFilePath.substring(dockerFilePath.lastIndexOf("/") + 1),
});
NODE_VERSION=12.22.12
HOSTNAME=017e4d07ee41
YARN_VERSION=1.22.18
SHLVL=1
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
FOO=bar
PWD=/

As described in the issue, I can work on a PR, but I need to know which option to choose.
I prefer the first one because it avoids duplication.

Screenshot 2024-07-21 at 11 05 55 Screenshot 2024-07-21 at 10 47 40

Curious to hear your thoughts on that!

@Siumauricio
Copy link
Contributor

@lorenzomigliorero I understand, wouldn't it be easier and better to pass the enviroment variables and convert them to an object and pass them directly to the ARGS? so we don't have to create a new UI or new fields?

@lorenzomigliorero
Copy link
Contributor Author

lorenzomigliorero commented Jul 21, 2024

@Siumauricio It's easier for sure, but not better. Sharing all environment variables as build arguments in a Docker build command can have implications for both security and performance. We should share only the needed ones. I'd love to prepare a PR of the first screenshot above. I'm comfortable with the project stack, and I appreciate the project, so I want to contribute. What do you think?

@Siumauricio
Copy link
Contributor

@lorenzomigliorero Thanks, if you have time would be awesome, use the second implementation you shared, and show only when we select the Dockerfile as BuildType

@lorenzomigliorero
Copy link
Contributor Author

Sure thing, I'll let you know soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants