Merge pull request #1502 from Dokploy/1493-railpack-spawns-multiple-build-kit-containers

1493 railpack spawns multiple build kit containers
This commit is contained in:
Mauricio Siu
2025-03-15 20:45:47 -06:00
committed by GitHub
2 changed files with 175 additions and 114 deletions

View File

@@ -136,26 +136,24 @@ export const getContainersByAppNameMatch = async (
result = stdout.trim().split("\n"); result = stdout.trim().split("\n");
} }
const containers = result const containers = result.map((line) => {
.map((line) => { const parts = line.split(" | ");
const parts = line.split(" | "); const containerId = parts[0]
const containerId = parts[0] ? parts[0].replace("CONTAINER ID : ", "").trim()
? parts[0].replace("CONTAINER ID : ", "").trim() : "No container id";
: "No container id"; const name = parts[1]
const name = parts[1] ? parts[1].replace("Name: ", "").trim()
? parts[1].replace("Name: ", "").trim() : "No container name";
: "No container name";
const state = parts[2] const state = parts[2]
? parts[2].replace("State: ", "").trim() ? parts[2].replace("State: ", "").trim()
: "No state"; : "No state";
return { return {
containerId, containerId,
name, name,
state, state,
}; };
}) });
.sort((a, b) => a.name.localeCompare(b.name));
return containers || []; return containers || [];
} catch (_error) {} } catch (_error) {}
@@ -192,30 +190,28 @@ export const getStackContainersByAppName = async (
result = stdout.trim().split("\n"); result = stdout.trim().split("\n");
} }
const containers = result const containers = result.map((line) => {
.map((line) => { const parts = line.split(" | ");
const parts = line.split(" | "); const containerId = parts[0]
const containerId = parts[0] ? parts[0].replace("CONTAINER ID : ", "").trim()
? parts[0].replace("CONTAINER ID : ", "").trim() : "No container id";
: "No container id"; const name = parts[1]
const name = parts[1] ? parts[1].replace("Name: ", "").trim()
? parts[1].replace("Name: ", "").trim() : "No container name";
: "No container name";
const state = parts[2] const state = parts[2]
? parts[2].replace("State: ", "").trim().toLowerCase() ? parts[2].replace("State: ", "").trim().toLowerCase()
: "No state"; : "No state";
const node = parts[3] const node = parts[3]
? parts[3].replace("Node: ", "").trim() ? parts[3].replace("Node: ", "").trim()
: "No specific node"; : "No specific node";
return { return {
containerId, containerId,
name, name,
state, state,
node, node,
}; };
}) });
.sort((a, b) => a.name.localeCompare(b.name));
return containers || []; return containers || [];
} catch (_error) {} } catch (_error) {}
@@ -253,31 +249,29 @@ export const getServiceContainersByAppName = async (
result = stdout.trim().split("\n"); result = stdout.trim().split("\n");
} }
const containers = result const containers = result.map((line) => {
.map((line) => { const parts = line.split(" | ");
const parts = line.split(" | "); const containerId = parts[0]
const containerId = parts[0] ? parts[0].replace("CONTAINER ID : ", "").trim()
? parts[0].replace("CONTAINER ID : ", "").trim() : "No container id";
: "No container id"; const name = parts[1]
const name = parts[1] ? parts[1].replace("Name: ", "").trim()
? parts[1].replace("Name: ", "").trim() : "No container name";
: "No container name";
const state = parts[2] const state = parts[2]
? parts[2].replace("State: ", "").trim().toLowerCase() ? parts[2].replace("State: ", "").trim().toLowerCase()
: "No state"; : "No state";
const node = parts[3] const node = parts[3]
? parts[3].replace("Node: ", "").trim() ? parts[3].replace("Node: ", "").trim()
: "No specific node"; : "No specific node";
return { return {
containerId, containerId,
name, name,
state, state,
node, node,
}; };
}) });
.sort((a, b) => a.name.localeCompare(b.name));
return containers || []; return containers || [];
} catch (_error) {} } catch (_error) {}
@@ -318,25 +312,23 @@ export const getContainersByAppLabel = async (
const lines = stdout.trim().split("\n"); const lines = stdout.trim().split("\n");
const containers = lines const containers = lines.map((line) => {
.map((line) => { const parts = line.split(" | ");
const parts = line.split(" | "); const containerId = parts[0]
const containerId = parts[0] ? parts[0].replace("CONTAINER ID : ", "").trim()
? parts[0].replace("CONTAINER ID : ", "").trim() : "No container id";
: "No container id"; const name = parts[1]
const name = parts[1] ? parts[1].replace("Name: ", "").trim()
? parts[1].replace("Name: ", "").trim() : "No container name";
: "No container name"; const state = parts[2]
const state = parts[2] ? parts[2].replace("State: ", "").trim()
? parts[2].replace("State: ", "").trim() : "No state";
: "No state"; return {
return { containerId,
containerId, name,
name, state,
state, };
}; });
})
.sort((a, b) => a.name.localeCompare(b.name));
return containers || []; return containers || [];
} catch (_error) {} } catch (_error) {}

View File

@@ -3,7 +3,6 @@ import type { ApplicationNested } from ".";
import { prepareEnvironmentVariables } from "../docker/utils"; import { prepareEnvironmentVariables } from "../docker/utils";
import { getBuildAppDirectory } from "../filesystem/directory"; import { getBuildAppDirectory } from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync"; import { spawnAsync } from "../process/spawnAsync";
import { execAsync } from "../process/execAsync";
export const buildRailpack = async ( export const buildRailpack = async (
application: ApplicationNested, application: ApplicationNested,
@@ -17,32 +16,62 @@ export const buildRailpack = async (
); );
try { try {
// Ensure buildkit container is running, create if it doesn't exist // First prepare the build plan and info
await execAsync( const prepareArgs = [
"docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit", "prepare",
); buildAppDirectory,
"--plan-out",
`${buildAppDirectory}/railpack-plan.json`,
"--info-out",
`${buildAppDirectory}/railpack-info.json`,
];
// Build the application using railpack // Add environment variables to prepare command
const args = ["build", buildAppDirectory, "--name", appName];
// Add environment variables
for (const env of envVariables) { for (const env of envVariables) {
args.push("--env", env); prepareArgs.push("--env", env);
} }
// Run prepare command
await spawnAsync("railpack", prepareArgs, (data) => {
if (writeStream.writable) {
writeStream.write(data);
}
});
// Build with BuildKit using the Railpack frontend
const buildArgs = [
"buildx",
"build",
"--build-arg",
"BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.0.55",
"-f",
`${buildAppDirectory}/railpack-plan.json`,
"--output",
`type=docker,name=${appName}`,
];
// Add secrets properly formatted
const env: { [key: string]: string } = {};
for (const envVar of envVariables) {
const [key, value] = envVar.split("=");
if (key && value) {
buildArgs.push("--secret", `id=${key},env=${key}`);
env[key] = value;
}
}
buildArgs.push(buildAppDirectory);
await spawnAsync( await spawnAsync(
"railpack", "docker",
args, buildArgs,
(data) => { (data) => {
if (writeStream.writable) { if (writeStream.writable) {
writeStream.write(data); writeStream.write(data);
} }
}, },
{ {
env: { env: { ...process.env, ...env },
...process.env,
BUILDKIT_HOST: "docker-container://buildkit",
},
}, },
); );
@@ -63,25 +92,65 @@ export const getRailpackCommand = (
application.project.env, application.project.env,
); );
// Build the application using railpack // Prepare command
const args = ["build", buildAppDirectory, "--name", appName]; const prepareArgs = [
"prepare",
buildAppDirectory,
"--plan-out",
`${buildAppDirectory}/railpack-plan.json`,
"--info-out",
`${buildAppDirectory}/railpack-info.json`,
];
// Add environment variables
for (const env of envVariables) { for (const env of envVariables) {
args.push("--env", env); prepareArgs.push("--env", env);
} }
const command = `railpack ${args.join(" ")}`; // Build command
const buildArgs = [
"buildx",
"build",
"--build-arg",
"BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.0.55",
"-f",
`${buildAppDirectory}/railpack-plan.json`,
"--output",
`type=docker,name=${appName}`,
];
// Add secrets properly formatted
const exportEnvs = [];
for (const envVar of envVariables) {
const [key, value] = envVar.split("=");
if (key && value) {
buildArgs.push("--secret", `id=${key},env=${key}`);
exportEnvs.push(`export ${key}=${value}`);
}
}
buildArgs.push(buildAppDirectory);
const bashCommand = ` const bashCommand = `
echo "Building with Railpack..." >> "${logPath}"; # Ensure we have a builder with containerd
docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit; docker buildx create --use --name builder-containerd --driver docker-container || true
export BUILDKIT_HOST=docker-container://buildkit; docker buildx use builder-containerd
${command} >> ${logPath} 2>> ${logPath} || {
echo " Railpack build failed" >> ${logPath}; echo "Preparing Railpack build plan..." >> "${logPath}";
exit 1; railpack ${prepareArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
} echo "❌ Railpack prepare failed" >> ${logPath};
echo "✅ Railpack build completed." >> ${logPath}; exit 1;
`; }
echo "✅ Railpack prepare completed." >> ${logPath};
echo "Building with Railpack frontend..." >> "${logPath}";
# Export environment variables for secrets
${exportEnvs.join("\n")}
docker ${buildArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
echo "❌ Railpack build failed" >> ${logPath};
exit 1;
}
echo "✅ Railpack build completed." >> ${logPath};
`;
return bashCommand; return bashCommand;
}; };