6426eedab5
Dockerfile 支持: - 基础镜像 (FROM) + 多阶段构建 + 平台架构 - 元数据 LABEL / 构建参数 ARG - 环境变量 ENV / 工作目录 WORKDIR - 文件复制 COPY + chown/chmod + ADD (自动解压/URL) - 依赖安装: apk/apt/yum/dnf 包管理器 + 应用依赖 + 构建命令 - 端口 EXPOSE / 数据卷 VOLUME - 用户 USER (非 root 运行) - 健康检查 HEALTHCHECK (interval/timeout/retries/start-period) - 入口 ENTRYPOINT / CMD (exec/shell 格式) - 停止信号 STOPSIGNAL / Shell 指令
365 lines
17 KiB
JavaScript
365 lines
17 KiB
JavaScript
export const dockerfileSchema = {
|
|
id: 'dockerfile',
|
|
name: 'Dockerfile',
|
|
icon: 'Box',
|
|
category: '容器化',
|
|
description: 'Docker 容器镜像构建文件',
|
|
format: 'dockerfile',
|
|
fileName: 'Dockerfile',
|
|
groups: [
|
|
{
|
|
title: '基础镜像',
|
|
fields: [
|
|
{ key: 'baseImage', label: '基础镜像', type: 'text', placeholder: 'nginx:1.24-alpine', default: 'nginx:1.24-alpine', required: true, tip: 'FROM 指令' },
|
|
{ key: 'fromPlatform', label: '平台架构', type: 'select', options: [
|
|
{ label: '不限制', value: '' },
|
|
{ label: 'linux/amd64', value: 'linux/amd64' },
|
|
{ label: 'linux/arm64', value: 'linux/arm64' },
|
|
], default: '', tip: '多架构构建时指定' },
|
|
{ key: 'enableMultiStage', label: '多阶段构建', type: 'switch', default: false, tip: '分离构建和运行阶段' },
|
|
{ key: 'builderImage', label: '构建阶段镜像', type: 'text', placeholder: 'node:18-alpine', default: 'node:18-alpine', dependsOn: { key: 'enableMultiStage', value: true } },
|
|
{ key: 'builderAs', label: '构建阶段别名', type: 'text', default: 'builder', dependsOn: { key: 'enableMultiStage', value: true } },
|
|
],
|
|
},
|
|
{
|
|
title: '元数据 (LABEL)',
|
|
fields: [
|
|
{ key: 'enableLabels', label: '添加标签', type: 'switch', default: false },
|
|
{ key: 'labelMaintainer', label: '维护者', type: 'text', placeholder: 'team@example.com', default: 'team@example.com', dependsOn: { key: 'enableLabels', value: true } },
|
|
{ key: 'labelVersion', label: '版本', type: 'text', placeholder: '1.0.0', default: '1.0.0', dependsOn: { key: 'enableLabels', value: true } },
|
|
{ key: 'labelDescription', label: '描述', type: 'text', placeholder: 'My application', default: '', dependsOn: { key: 'enableLabels', value: true } },
|
|
],
|
|
},
|
|
{
|
|
title: '构建参数 (ARG)',
|
|
fields: [
|
|
{ key: 'enableArgs', label: '定义构建参数', type: 'switch', default: false, tip: '仅在 docker build --build-arg 时生效' },
|
|
{ key: 'args', label: '参数列表 (KEY=VALUE)', type: 'text', placeholder: 'APP_VERSION=1.0.0\nNODE_ENV=production', default: 'APP_VERSION=1.0.0', dependsOn: { key: 'enableArgs', value: true }, tip: '每行一个,格式 KEY 或 KEY=默认值' },
|
|
],
|
|
},
|
|
{
|
|
title: '环境变量 (ENV)',
|
|
fields: [
|
|
{ key: 'enableEnv', label: '设置环境变量', type: 'switch', default: false },
|
|
{ key: 'envVars', label: '环境变量 (KEY=VALUE)', type: 'text', placeholder: 'NODE_ENV=production\nAPP_PORT=8080', default: 'NODE_ENV=production\nAPP_PORT=8080', dependsOn: { key: 'enableEnv', value: true }, tip: '每行一个,格式 KEY=VALUE' },
|
|
],
|
|
},
|
|
{
|
|
title: '工作目录与用户',
|
|
fields: [
|
|
{ key: 'enableWorkdir', label: '设置工作目录', type: 'switch', default: true },
|
|
{ key: 'workdir', label: '工作目录', type: 'text', default: '/app', dependsOn: { key: 'enableWorkdir', value: true } },
|
|
{ key: 'enableUser', label: '切换运行用户', type: 'switch', default: false, tip: '生产环境建议使用非 root 用户' },
|
|
{ key: 'userName', label: '用户名或 UID', type: 'text', placeholder: '1001:1001', default: '1001:1001', dependsOn: { key: 'enableUser', value: true } },
|
|
{ key: 'enableUseradd', label: '创建用户', type: 'switch', default: false, dependsOn: { key: 'enableUser', value: true }, tip: '在 RUN 中创建用户' },
|
|
{ key: 'createUserName', label: '用户名', type: 'text', default: 'appuser', dependsOn: { key: 'enableUseradd', value: true } },
|
|
],
|
|
},
|
|
{
|
|
title: '文件复制',
|
|
fields: [
|
|
{ key: 'enableCopy', label: '复制文件', type: 'switch', default: true },
|
|
{ key: 'copySrc', label: '源路径', type: 'text', placeholder: '. 或 ./dist', default: '.', dependsOn: { key: 'enableCopy', value: true } },
|
|
{ key: 'copyDest', label: '目标路径', type: 'text', placeholder: '/app', default: '/app', dependsOn: { key: 'enableCopy', value: true } },
|
|
{ key: 'enableCopyChown', label: '设置文件所有者', type: 'switch', default: false, dependsOn: { key: 'enableCopy', value: true } },
|
|
{ key: 'copyChown', label: '所有者', type: 'text', placeholder: '1001:1001', default: '1001:1001', dependsOn: { key: 'enableCopyChown', value: true } },
|
|
{ key: 'enableCopyChmod', label: '设置文件权限', type: 'switch', default: false, dependsOn: { key: 'enableCopy', value: true } },
|
|
{ key: 'copyChmod', label: '权限', type: 'text', placeholder: '755', default: '755', dependsOn: { key: 'enableCopyChmod', value: true } },
|
|
{ key: 'enableAdd', label: '使用 ADD (自动解压/URL)', type: 'switch', default: false, tip: '需要解压 tar 或下载远程文件时使用' },
|
|
{ key: 'addSrc', label: 'ADD 源', type: 'text', placeholder: 'https://example.com/app.tar.gz', default: '', dependsOn: { key: 'enableAdd', value: true } },
|
|
{ key: 'addDest', label: 'ADD 目标', type: 'text', default: '/app/', dependsOn: { key: 'enableAdd', value: true } },
|
|
],
|
|
},
|
|
{
|
|
title: '依赖安装 (RUN)',
|
|
fields: [
|
|
{ key: 'packageManager', label: '包管理器', type: 'select', options: [
|
|
{ label: '不安装系统包', value: 'none' },
|
|
{ label: 'apk (Alpine)', value: 'apk' },
|
|
{ label: 'apt (Debian/Ubuntu)', value: 'apt' },
|
|
{ label: 'yum (CentOS/RHEL)', value: 'yum' },
|
|
{ label: 'dnf (Fedora)', value: 'dnf' },
|
|
], default: 'none' },
|
|
{ key: 'systemPackages', label: '系统包 (空格分隔)', type: 'text', placeholder: 'curl wget tzdata', default: 'curl ca-certificates tzdata', dependsOn: { key: 'packageManager', valueNotIn: ['none'] } },
|
|
{ key: 'enableAppInstall', label: '安装应用依赖', type: 'switch', default: false, tip: 'npm install / pip install / go build 等' },
|
|
{ key: 'appInstallCmd', label: '安装命令', type: 'text', placeholder: 'npm ci --only=production', default: 'npm ci --only=production', dependsOn: { key: 'enableAppInstall', value: true } },
|
|
{ key: 'enableBuildCmd', label: '构建命令', type: 'switch', default: false, tip: 'npm run build / go build / mvn package 等' },
|
|
{ key: 'buildCmd', label: '构建命令', type: 'text', placeholder: 'npm run build', default: 'npm run build', dependsOn: { key: 'enableBuildCmd', value: true } },
|
|
{ key: 'enableCleanup', label: '清理缓存', type: 'switch', default: true, dependsOn: { key: 'packageManager', valueNotIn: ['none'] }, tip: '减小镜像体积' },
|
|
],
|
|
},
|
|
{
|
|
title: '端口与网络',
|
|
fields: [
|
|
{ key: 'enableExpose', label: '暴露端口', type: 'switch', default: true },
|
|
{ key: 'exposePorts', label: '端口列表', type: 'text', placeholder: '80 443', default: '80', dependsOn: { key: 'enableExpose', value: true }, tip: '空格分隔多个端口' },
|
|
],
|
|
},
|
|
{
|
|
title: '健康检查 (HEALTHCHECK)',
|
|
fields: [
|
|
{ key: 'enableHealthcheck', label: '添加健康检查', type: 'switch', default: false },
|
|
{ key: 'healthcheckType', label: '检查方式', type: 'select', options: [
|
|
{ label: 'CMD (自定义命令)', value: 'CMD' },
|
|
{ label: 'NONE (禁用继承的健康检查)', value: 'NONE' },
|
|
], default: 'CMD', dependsOn: { key: 'enableHealthcheck', value: true } },
|
|
{ key: 'healthcheckInterval', label: '检查间隔', type: 'text', default: '30s', dependsOn: { key: 'healthcheckType', value: 'CMD' } },
|
|
{ key: 'healthcheckTimeout', label: '超时时间', type: 'text', default: '10s', dependsOn: { key: 'healthcheckType', value: 'CMD' } },
|
|
{ key: 'healthcheckRetries', label: '重试次数', type: 'number', min: 1, max: 10, default: 3, dependsOn: { key: 'healthcheckType', value: 'CMD' } },
|
|
{ key: 'healthcheckStartPeriod', label: '启动等待期', type: 'text', default: '5s', dependsOn: { key: 'healthcheckType', value: 'CMD' } },
|
|
{ key: 'healthcheckCmd', label: '检查命令', type: 'text', placeholder: 'curl -f http://localhost/ || exit 1', default: 'curl -f http://localhost:80/ || exit 1', dependsOn: { key: 'healthcheckType', value: 'CMD' }, required: true },
|
|
],
|
|
},
|
|
{
|
|
title: '挂载点与数据卷',
|
|
fields: [
|
|
{ key: 'enableVolume', label: '声明数据卷', type: 'switch', default: false },
|
|
{ key: 'volumePaths', label: '挂载路径', type: 'text', placeholder: '/data /var/log/app', default: '/data', dependsOn: { key: 'enableVolume', value: true }, tip: '空格分隔多个路径' },
|
|
],
|
|
},
|
|
{
|
|
title: '入口与启动',
|
|
fields: [
|
|
{ key: 'enableEntrypoint', label: '设置 ENTRYPOINT', type: 'switch', default: false, tip: '容器启动时执行的固定命令' },
|
|
{ key: 'entrypointCmd', label: 'ENTRYPOINT 命令', type: 'text', placeholder: '["java", "-jar", "app.jar"]', default: '["node", "server.js"]', dependsOn: { key: 'enableEntrypoint', value: true }, tip: 'JSON 数组格式或 shell 格式' },
|
|
{ key: 'enableCmd', label: '设置 CMD', type: 'switch', default: true, tip: '容器启动时的默认参数' },
|
|
{ key: 'cmdType', label: 'CMD 格式', type: 'select', options: [
|
|
{ label: 'exec 格式 (推荐)', value: 'exec' },
|
|
{ label: 'shell 格式', value: 'shell' },
|
|
], default: 'exec', dependsOn: { key: 'enableCmd', value: true } },
|
|
{ key: 'cmdValue', label: 'CMD 内容', type: 'text', placeholder: '["nginx", "-g", "daemon off;"]', default: '["nginx", "-g", "daemon off;"]', dependsOn: { key: 'enableCmd', value: true } },
|
|
],
|
|
},
|
|
{
|
|
title: '信号与停止',
|
|
fields: [
|
|
{ key: 'enableStopSignal', label: '自定义停止信号', type: 'switch', default: false, tip: 'docker stop 发送的信号' },
|
|
{ key: 'stopSignal', label: '停止信号', type: 'select', options: [
|
|
{ label: 'SIGTERM (默认)', value: 'SIGTERM' },
|
|
{ label: 'SIGQUIT', value: 'SIGQUIT' },
|
|
{ label: 'SIGINT', value: 'SIGINT' },
|
|
{ label: 'SIGUSR1', value: 'SIGUSR1' },
|
|
], default: 'SIGTERM', dependsOn: { key: 'enableStopSignal', value: true } },
|
|
{ key: 'enableShell', label: '指定 Shell', type: 'switch', default: false, tip: 'SHELL 指令,RUN 使用的 shell' },
|
|
{ key: 'shellPath', label: 'Shell 路径', type: 'text', default: '/bin/sh', dependsOn: { key: 'enableShell', value: true } },
|
|
{ key: 'shellFlag', label: 'Shell 参数', type: 'text', default: '-c', dependsOn: { key: 'enableShell', value: true } },
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
export function generateDockerfile(config) {
|
|
const lines = []
|
|
lines.push(`# Dockerfile - 由 ConfTemplate 生成`)
|
|
lines.push(`# 生成时间: ${new Date().toLocaleString('zh-CN')}`)
|
|
lines.push(``)
|
|
|
|
// Builder stage
|
|
if (config.enableMultiStage) {
|
|
lines.push(`# ======================== 构建阶段 ========================`)
|
|
lines.push(`FROM${config.fromPlatform ? ` --platform=${config.fromPlatform}` : ''} ${config.builderImage} AS ${config.builderAs}`)
|
|
lines.push(``)
|
|
if (config.enableWorkdir) {
|
|
lines.push(`WORKDIR ${config.workdir}`)
|
|
}
|
|
lines.push(`COPY . .`)
|
|
if (config.enableAppInstall && config.appInstallCmd) {
|
|
lines.push(`RUN ${config.appInstallCmd}`)
|
|
}
|
|
if (config.enableBuildCmd && config.buildCmd) {
|
|
lines.push(`RUN ${config.buildCmd}`)
|
|
}
|
|
lines.push(``)
|
|
lines.push(`# ======================== 运行阶段 ========================`)
|
|
}
|
|
|
|
// FROM
|
|
const platformArg = config.fromPlatform ? ` --platform=${config.fromPlatform}` : ''
|
|
if (config.enableMultiStage) {
|
|
lines.push(`FROM${platformArg} ${config.baseImage}`)
|
|
} else {
|
|
lines.push(`FROM${platformArg} ${config.baseImage}`)
|
|
}
|
|
lines.push(``)
|
|
|
|
// ARG
|
|
if (config.enableArgs && config.args) {
|
|
config.args.split('\n').filter(Boolean).forEach(line => {
|
|
lines.push(`ARG ${line.trim()}`)
|
|
})
|
|
lines.push(``)
|
|
}
|
|
|
|
// LABEL
|
|
if (config.enableLabels) {
|
|
if (config.labelMaintainer) lines.push(`LABEL maintainer="${config.labelMaintainer}"`)
|
|
if (config.labelVersion) lines.push(`LABEL version="${config.labelVersion}"`)
|
|
if (config.labelDescription) lines.push(`LABEL description="${config.labelDescription}"`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// ENV
|
|
if (config.enableEnv && config.envVars) {
|
|
config.envVars.split('\n').filter(Boolean).forEach(line => {
|
|
lines.push(`ENV ${line.trim()}`)
|
|
})
|
|
lines.push(``)
|
|
}
|
|
|
|
// WORKDIR
|
|
if (config.enableWorkdir) {
|
|
lines.push(`WORKDIR ${config.workdir}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// Package install
|
|
if (config.packageManager && config.packageManager !== 'none') {
|
|
const pkgs = (config.systemPackages || '').trim()
|
|
if (pkgs) {
|
|
if (config.packageManager === 'apk') {
|
|
lines.push(`RUN apk add --no-cache ${pkgs}`)
|
|
} else if (config.packageManager === 'apt') {
|
|
lines.push(`RUN apt-get update && \\`)
|
|
lines.push(` apt-get install -y --no-install-recommends ${pkgs} && \\`)
|
|
if (config.enableCleanup) {
|
|
lines.push(` rm -rf /var/lib/apt/lists/*`)
|
|
}
|
|
} else if (config.packageManager === 'yum') {
|
|
lines.push(`RUN yum install -y ${pkgs} && \\`)
|
|
if (config.enableCleanup) {
|
|
lines.push(` yum clean all && rm -rf /var/cache/yum`)
|
|
}
|
|
} else if (config.packageManager === 'dnf') {
|
|
lines.push(`RUN dnf install -y ${pkgs} && \\`)
|
|
if (config.enableCleanup) {
|
|
lines.push(` dnf clean all && rm -rf /var/cache/dnf`)
|
|
}
|
|
}
|
|
lines.push(``)
|
|
}
|
|
}
|
|
|
|
// Create user
|
|
if (config.enableUser && config.enableUseradd && config.createUserName) {
|
|
lines.push(`RUN addgroup -S ${config.createUserName} && adduser -S ${config.createUserName} -G ${config.createUserName}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// COPY from builder stage
|
|
if (config.enableMultiStage) {
|
|
if (config.enableCopyChown) {
|
|
lines.push(`COPY --from=${config.builderAs} --chown=${config.copyChown} ${config.workdir || '/app'} ${config.copyDest}`)
|
|
} else {
|
|
lines.push(`COPY --from=${config.builderAs} ${config.workdir || '/app'} ${config.copyDest}`)
|
|
}
|
|
lines.push(``)
|
|
}
|
|
|
|
// COPY
|
|
if (config.enableCopy && !config.enableMultiStage) {
|
|
let copyFlags = []
|
|
if (config.enableCopyChown && config.copyChown) copyFlags.push(`--chown=${config.copyChown}`)
|
|
if (config.enableCopyChmod && config.copyChmod) copyFlags.push(`--chmod=${config.copyChmod}`)
|
|
const flags = copyFlags.length ? ` ${copyFlags.join(' ')}` : ''
|
|
lines.push(`COPY${flags} ${config.copySrc} ${config.copyDest}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// ADD
|
|
if (config.enableAdd && config.addSrc) {
|
|
lines.push(`ADD ${config.addSrc} ${config.addDest}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// App install (non-multi-stage)
|
|
if (!config.enableMultiStage && config.enableAppInstall && config.appInstallCmd) {
|
|
lines.push(`RUN ${config.appInstallCmd}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// Build cmd (non-multi-stage)
|
|
if (!config.enableMultiStage && config.enableBuildCmd && config.buildCmd) {
|
|
lines.push(`RUN ${config.buildCmd}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// VOLUME
|
|
if (config.enableVolume && config.volumePaths) {
|
|
const paths = config.volumePaths.split(/\s+/).filter(Boolean)
|
|
if (paths.length === 1) {
|
|
lines.push(`VOLUME ["${paths[0]}"]`)
|
|
} else {
|
|
lines.push(`VOLUME [${paths.map(p => `"${p}"`).join(', ')}]`)
|
|
}
|
|
lines.push(``)
|
|
}
|
|
|
|
// EXPOSE
|
|
if (config.enableExpose && config.exposePorts) {
|
|
lines.push(`EXPOSE ${config.exposePorts}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// USER
|
|
if (config.enableUser) {
|
|
lines.push(`USER ${config.userName}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// HEALTHCHECK
|
|
if (config.enableHealthcheck) {
|
|
if (config.healthcheckType === 'NONE') {
|
|
lines.push(`HEALTHCHECK NONE`)
|
|
} else {
|
|
let hc = `HEALTHCHECK`
|
|
if (config.healthcheckInterval) hc += ` --interval=${config.healthcheckInterval}`
|
|
if (config.healthcheckTimeout) hc += ` --timeout=${config.healthcheckTimeout}`
|
|
if (config.healthcheckRetries) hc += ` --retries=${config.healthcheckRetries}`
|
|
if (config.healthcheckStartPeriod) hc += ` --start-period=${config.healthcheckStartPeriod}`
|
|
hc += ` CMD ${config.healthcheckCmd}`
|
|
lines.push(hc)
|
|
}
|
|
lines.push(``)
|
|
}
|
|
|
|
// STOPSIGNAL
|
|
if (config.enableStopSignal) {
|
|
lines.push(`STOPSIGNAL ${config.stopSignal}`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// SHELL
|
|
if (config.enableShell) {
|
|
lines.push(`SHELL ["${config.shellPath}", "${config.shellFlag}"]`)
|
|
lines.push(``)
|
|
}
|
|
|
|
// ENTRYPOINT
|
|
if (config.enableEntrypoint && config.entrypointCmd) {
|
|
const val = config.entrypointCmd.trim()
|
|
if (val.startsWith('[')) {
|
|
lines.push(`ENTRYPOINT ${val}`)
|
|
} else {
|
|
lines.push(`ENTRYPOINT ["${val}"]`)
|
|
}
|
|
lines.push(``)
|
|
}
|
|
|
|
// CMD
|
|
if (config.enableCmd && config.cmdValue) {
|
|
const val = config.cmdValue.trim()
|
|
if (config.cmdType === 'exec') {
|
|
if (val.startsWith('[')) {
|
|
lines.push(`CMD ${val}`)
|
|
} else {
|
|
lines.push(`CMD ["${val}"]`)
|
|
}
|
|
} else {
|
|
lines.push(`CMD ${val}`)
|
|
}
|
|
lines.push(``)
|
|
}
|
|
|
|
return lines.join('\n')
|
|
}
|