From b5c55858690943d2c0205c76aed08cfba898c48f Mon Sep 17 00:00:00 2001 From: ymhua Date: Thu, 4 Dec 2025 09:48:22 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96ktib=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ktib/app/init.go | 78 +++++++++++-------- cmd/ktib/app/make.go | 23 +++--- docs/commands/project.md | 136 ++++++++++++++++++++++++++------- pkg/project/baseimage_build.go | 114 +++++++++++++++------------ pkg/project/bootstrap.go | 11 ++- pkg/project/config_rootfs.go | 25 ++++++ pkg/templates/dockerfile.go | 7 ++ pkg/templates/python_config.go | 12 +++ pkg/utils/image_type.go | 12 +++ 9 files changed, 297 insertions(+), 121 deletions(-) create mode 100644 pkg/templates/python_config.go create mode 100644 pkg/utils/image_type.go diff --git a/cmd/ktib/app/init.go b/cmd/ktib/app/init.go index 02e57e2..7f07ba9 100644 --- a/cmd/ktib/app/init.go +++ b/cmd/ktib/app/init.go @@ -17,6 +17,7 @@ import ( "strings" "gitee.com/openeuler/ktib/pkg/project" + "gitee.com/openeuler/ktib/pkg/utils" "github.com/sirupsen/logrus" "github.com/spf13/cobra" //"gopkg.in/yaml.v2" @@ -201,16 +202,27 @@ func newSubCmdInit() *cobra.Command { var option InitOption cmd := &cobra.Command{ Use: "init", - Short: "Run this command in order to init a project structure", - Long: `Init command helps you create an empty project with specified options. -It creates the necessary directory structure and files to kickstart your project.`, - Example: ` # Create a project structure - ktib project init /path/to/project`, + Short: "Initialize an image project directory structure", + Long: "Init creates the directory skeleton (dockerfile/, rootfs/, files/, tests/) and lays down default templates like Dockerfile, README, removeminimallist and unmaskService.", + Example: ` # Create a new project skeleton + ktib project init /path/to/project + + # Init with type and write a default config file + ktib project init --type init /path/to/project + + # Build rootfs using the generated config + ktib project build-rootfs --config /path/to/project/config.yml /path/to/project`, RunE: func(cmd *cobra.Command, args []string) error { return runInit(cmd, args, option) }, Args: cobra.MinimumNArgs(1), } + types := utils.ValidImageTypes + flags := cmd.Flags() + flags.StringVar(&option.BuildType, "type", "platform", "Type of image ("+strings.Join(types, ", ")+")") + cmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return types, cobra.ShellCompDirectiveDefault + }) return cmd } @@ -222,11 +234,18 @@ func runInit(c *cobra.Command, args []string, option InitOption) error { } boot := project.NewBootstrap(args[0]) + if option.BuildType != "" { + validTypes := utils.ValidImageTypes + if !utils.IsValidImageType(option.BuildType) { + return fmt.Errorf("无效的镜像类型: %s。有效的类型包括: %s", option.BuildType, strings.Join(validTypes, ", ")) + } + boot.BuildType = option.BuildType + } + // 使用新的方法初始化项目结构 if err := boot.InitProjectStructure(); err != nil { return err } - return nil } @@ -238,11 +257,13 @@ func newSubCmdBuildRootfs() *cobra.Command { cmd := &cobra.Command{ Use: "build-rootfs", Aliases: []string{"br"}, - Short: "Run this command to build rootfs for a project", - Long: `Build-rootfs command helps you create a rootfs for your project based on the configuration. -It requires a config file that specifies the packages and settings for the rootfs.`, + Short: "Build rootfs according to the given config.yml", + Long: "Build-rootfs installs packages into the project rootfs using yum/dnf with nodocs, sets network/locale/timezone and initializes machine-id. A config file is required.", Example: ` # Build rootfs for a project - ktib project build-rootfs --config config.yml /path/to/project`, + ktib project build-rootfs --config /path/to/project/config.yml /path/to/project + + # Generate a default config first + ktib project default_config > /path/to/project/config.yml`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { logrus.Println("The number of parameters passed in is incorrect") @@ -270,17 +291,18 @@ func newSubCmdCleanRootfs() *cobra.Command { imageType string } - // 定义有效的镜像类型 - validImageTypes := []string{"micro", "minimal", "platform", "init"} + validImageTypes := utils.ValidImageTypes cmd := &cobra.Command{ Use: "clean-rootfs", Aliases: []string{"cr"}, - Short: "Run this command to clean unnecessary files and packages in rootfs", - Long: `Clean-rootfs command helps you remove unnecessary files and packages from your rootfs. -It also performs additional environment configuration operations to optimize the image size.`, - Example: ` # Clean rootfs for a project - ktib project clean-rootfs --type minimal /path/to/project`, + Short: "Clean rootfs by removing files, optional packages, and unmasking services", + Long: "Clean-rootfs removes locales/docs/caches/logs/tmp, optionally removes packages per type (e.g., minimal), unmask services, and performs final cleanup. Use --type to apply type-specific rules.", + Example: ` # Clean rootfs with minimal rules + ktib project clean-rootfs --type minimal /path/to/project + + # Clean rootfs for init/platform types + ktib project clean-rootfs --type init /path/to/project`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { logrus.Println("The number of parameters passed in is incorrect") @@ -291,20 +313,10 @@ It also performs additional environment configuration operations to optimize the // 如果指定了镜像类型,则进行校验并设置 if option.imageType != "" { - // 校验镜像类型 - valid := false - for _, t := range validImageTypes { - if option.imageType == t { - valid = true - break - } - } - if !valid { + if !utils.IsValidImageType(option.imageType) { return fmt.Errorf("无效的镜像类型: %s。有效的类型包括: %s", option.imageType, strings.Join(validImageTypes, ", ")) } - - // 设置镜像类型 boot.BuildType = option.imageType } @@ -344,11 +356,13 @@ func newSubCmdBuild() *cobra.Command { } cmd := &cobra.Command{ Use: "build", - Short: "Run this command to build a container image from rootfs", - Long: `Build command helps you create a container image using the rootfs and Dockerfile. -It packages the rootfs into a container image that can be used with container runtimes.`, + Short: "Build the container image from the prepared rootfs", + Long: "Build packages rootfs.tar, resolves Dockerfile (prefers project/dockerfile/Dockerfile) and uses buildah to produce the image with the given name:tag.", Example: ` # Build container image for a project - ktib project build --name myimage --tag latest /path/to/project`, + ktib project build --name myimage --tag latest /path/to/project + + # Build with defaults + ktib project build /path/to/project`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { logrus.Println("The number of parameters passed in is incorrect") diff --git a/cmd/ktib/app/make.go b/cmd/ktib/app/make.go index d61ebbb..297590a 100644 --- a/cmd/ktib/app/make.go +++ b/cmd/ktib/app/make.go @@ -17,6 +17,7 @@ import ( "strings" "gitee.com/openeuler/ktib/pkg/project" + "gitee.com/openeuler/ktib/pkg/utils" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -38,6 +39,12 @@ func runMake(cmd *cobra.Command, args []string, option makeOption) error { } if option.init { boot := project.NewBootstrap(args[0]) + if option.imageType != "" { + if !utils.IsValidImageType(option.imageType) { + return fmt.Errorf("无效的镜像类型: %s。有效的类型包括: %s", option.imageType, strings.Join(utils.ValidImageTypes, ", ")) + } + boot.BuildType = option.imageType + } if err := boot.InitProjectStructure(); err != nil { return err } @@ -51,20 +58,10 @@ func runMake(cmd *cobra.Command, args []string, option makeOption) error { if option.config == "" { return fmt.Errorf("when building rootfs, you need to specify the --config") } - - validImageTypes := []string{"micro", "minimal", "platform", "init"} - boot := project.NewBootstrap(args[0]) if option.imageType != "" { - valid := false - for _, t := range validImageTypes { - if option.imageType == t { - valid = true - break - } - } - if !valid { - return fmt.Errorf("无效的镜像类型: %s。有效的类型包括: %s", option.imageType, strings.Join(validImageTypes, ", ")) + if !utils.IsValidImageType(option.imageType) { + return fmt.Errorf("无效的镜像类型: %s。有效的类型包括: %s", option.imageType, strings.Join(utils.ValidImageTypes, ", ")) } boot.BuildType = option.imageType } @@ -127,7 +124,7 @@ func newCmdMake() *cobra.Command { flags.BoolVar(&options.init, "init", false, "init project structure before build; generate default config when not set") flags.StringVar(&options.imageType, "type", "platform", "Type of image (micro|minimal|platform|init)") cmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"micro", "minimal", "platform", "init"}, cobra.ShellCompDirectiveDefault + return utils.ValidImageTypes, cobra.ShellCompDirectiveDefault }) flags.StringVar(&options.imageName, "name", "ktib-image", "name of the container image") flags.StringVar(&options.tag, "tag", "latest", "tag of the container image") diff --git a/docs/commands/project.md b/docs/commands/project.md index e2bef1c..330b79c 100644 --- a/docs/commands/project.md +++ b/docs/commands/project.md @@ -6,6 +6,7 @@ - `build-rootfs` - `clean-rootfs` - `build` +- `make` ## 命令说明 @@ -17,25 +18,58 @@ ktib project default_config > config.yml ``` +**可选参数:** +- `--timezone`:设置时区(默认:`Asia/Shanghai`) +- `--locale`:设置语言环境(默认:`en_US.UTF-8`) + +**示例:** +```bash +# 生成默认配置示例 +ktib project default_config > config.yml + +# 指定时区 +ktib project default_config --timezone "America/New_York" > config.yml + +# 指定语言 +ktib project default_config --locale "zh_CN.UTF-8" > config.yml + +# 同时指定时区与语言 +ktib project default_config --timezone "Europe/London" --locale "en_GB.UTF-8" > config.yml +``` + **说明:** -- 该命令会输出一个默认的 YAML 配置模板到标准输出 -- 通常需要重定向到文件(如 `config.yml`)来保存配置 -- 配置模板包含 rootfs 构建所需的基本配置项 +- 输出一个默认的 YAML 配置模板到标准输出,通常配合重定向保存到文件 +- 模板包含 rootfs 构建所需的基本配置项(packages、network、locale、timezone) ### `init` -初始化项目结构。 +初始化项目结构并可选写入默认配置。 **用法:** ```bash -ktib project init <项目路径> +ktib project init [--type <镜像类型>] [--config <配置文件路径>] [--timezone <时区>] [--locale <语言>] <项目路径> ``` **参数:** +- `--type`:镜像类型(可选,有效值:`micro|minimal|platform|init`) +- `--config`:生成并写入默认配置文件到指定路径(可选) +- `--timezone`:默认配置中的时区(可选,默认:`Asia/Shanghai`) +- `--locale`:默认配置中的语言(可选,默认:`en_US.UTF-8`) - `<项目路径>`:要初始化项目的目录路径(必需) +**示例:** +```bash +# 创建项目骨架 +ktib project init /path/to/project + +# 指定类型并写入默认配置 +ktib project init --type init --config /path/to/project/config.yml --timezone "Asia/Shanghai" --locale "en_US.UTF-8" /path/to/project +``` + **说明:** -- 创建必要的项目目录结构和文件 -- 为后续的 rootfs 构建和镜像构建做准备 +- 创建目录结构:`dockerfile/`、`rootfs/`、`files/`、`tests/` +- 生成模板文件:`dockerfile/Dockerfile`、`README.md`、`files/removeminimallist`、`files/unmaskService` +- 当类型为 `init` 时,`Dockerfile` 默认使用 `CMD ["/sbin/init"]`,其他类型使用 `CMD ["/bin/bash"]` +- 如果提供 `--config`,初始化后会写入默认配置文件(可用 `build-rootfs` 使用) ### `build-rootfs` 构建项目的 rootfs。 @@ -51,16 +85,20 @@ ktib project build-rootfs --config <配置文件路径> <项目路径> **示例:** ```bash -ktib project build-rootfs --config config.yml /path/to/project +ktib project build-rootfs --config /path/to/project/config.yml /path/to/project + +# 先生成默认配置再构建 +ktib project default_config > /path/to/project/config.yml +ktib project build-rootfs --config /path/to/project/config.yml /path/to/project ``` **说明:** -- 根据配置文件构建 rootfs 文件系统 -- 配置文件指定了要安装的软件包和系统设置 -- 构建完成后会提示运行 `clean-rootfs` 命令进行清理 +- 按配置安装软件包到 `rootfs/`(`yum/dnf`、`nodocs`、禁用弱依赖、`group_package_types=mandatory`) +- 写入网络、`/etc/dnf/vars/infra=container`、语言与 `/etc/locale.conf`、时区软链 `/etc/localtime` 与 `/etc/timezone` +- 初始化空的 `/etc/machine-id`,复制 `bash` skeleton ### `clean-rootfs` -清理 rootfs 中不必要的文件和软件包。 +清理 rootfs 中不必要的文件和软件包,并执行类型化优化。 **用法:** ```bash @@ -68,7 +106,7 @@ ktib project clean-rootfs [--type <镜像类型>] <项目路径> ``` **参数:** -- `--type`:镜像类型(可选,有效值:micro、minimal、platform、init) +- `--type`:镜像类型(可选,有效值:`micro|minimal|platform|init`) - `<项目路径>`:项目目录路径(必需) **示例:** @@ -76,14 +114,18 @@ ktib project clean-rootfs [--type <镜像类型>] <项目路径> # 使用默认清理 ktib project clean-rootfs /path/to/project -# 指定镜像类型进行清理 +# 指定镜像类型进行清理(minimal 会根据 removeminimallist 移除包) ktib project clean-rootfs --type minimal /path/to/project + +# init/platform 类型的额外清理(pip 与 __pycache__) +ktib project clean-rootfs --type init /path/to/project ``` **说明:** -- 移除 rootfs 中不必要的文件和软件包 -- 执行额外的环境配置操作以优化镜像大小 -- 支持不同类型的镜像优化策略 +- 删除 locales、docs、icons、i18n、`var/cache/yum`、`var/cache/ldconfig`、日志与临时目录等 +- `minimal` 类型:根据 `files/removeminimallist` 在 chroot 中批量移除包 +- 解除服务屏蔽:按 `files/unmaskService` 在 chroot 中执行 +- `platform`/`init` 类型:在 chroot 中安装 `pip`(`ensurepip`)并删除全局 `__pycache__` ### `build` 从 rootfs 构建容器镜像。 @@ -94,8 +136,8 @@ ktib project build [--name <镜像名称>] [--tag <标签>] <项目路径> ``` **参数:** -- `--name`:容器镜像名称(可选,默认:ktib-image) -- `--tag`:镜像标签(可选,默认:latest) +- `--name`:容器镜像名称(可选,默认:`ktib-image`) +- `--tag`:镜像标签(可选,默认:`latest`) - `<项目路径>`:项目目录路径(必需) **示例:** @@ -108,24 +150,59 @@ ktib project build --name myimage --tag v1.0 /path/to/project ``` **说明:** -- 使用 rootfs 和 Dockerfile 构建容器镜像 -- 将 rootfs 打包成可用的容器镜像 -- 生成的镜像可用于容器运行时 +- 打包 `rootfs/` 为 `rootfs.tar`,解析并优先使用项目 `dockerfile/Dockerfile`;如不存在则按类型生成最小 `Dockerfile` +- `init` 类型默认 `CMD ["/sbin/init"]`,其他类型默认 `CMD ["/bin/bash"]` +- 使用 buildah 接口构建镜像(支持 `name:tag` 标签) + +### `make` +一键构建基础镜像:可选初始化项目、构建 rootfs、清理 rootfs、构建镜像。 + +**用法:** +```bash +ktib make [--init] [--config <配置文件>] [--type <镜像类型>] [--name <镜像名称>] [--tag <标签>] [--timezone <时区>] [--locale <语言>] <项目路径> +``` + +**参数:** +- `--init`:构建前初始化项目结构,若未提供 `--config` 则生成默认配置 +- `--config`:配置文件路径(可选,与 `--init` 联动可自动生成) +- `--type`:镜像类型(可选,默认:`platform`;有效值:`micro|minimal|platform|init`) +- `--name`:容器镜像名称(可选,默认:`ktib-image`) +- `--tag`:镜像标签(可选,默认:`latest`) +- `--timezone`:默认配置中的时区(可选,默认:`Asia/Shanghai`) +- `--locale`:默认配置中的语言(可选,默认:`en_US.UTF-8`) +- `<项目路径>`:项目目录路径(必需) + +**示例:** +```bash +# 初始化并一键构建(minimal 类型) +ktib make --init --type minimal --name myimage --tag latest /path/to/project + +# 指定配置一键构建 +ktib make --config /path/to/project/config.yml --type init --name myimage --tag latest /path/to/project + +# 初始化并一键构建,带自定义时区与语言 +ktib make --init --timezone "America/New_York" --locale "zh_CN.UTF-8" /path/to/project +``` + +**说明:** +- 当 `--init` 存在时:先初始化目录与模板,必要时生成默认配置 +- 随后按配置构建 rootfs、执行类型化清理(含 `minimal` 移包、`init/platform` pip 与 `__pycache__` 清理、解屏蔽服务) +- 最后打包并构建容器镜像,默认标签 `name:latest` ## 典型工作流程 1. **初始化项目:** ```bash - ktib project init /path/to/project + ktib project init --type platform /path/to/project ``` 2. **生成配置:** ```bash - ktib project default_config > config.yml + ktib project default_config --type platform > config.yml ``` 3. **构建 rootfs:** ```bash - ktib project build-rootfs --config config.yml /path/to/project + ktib project build-rootfs --config config.yml --type platform /path/to/project ``` 4. **清理 rootfs:** @@ -135,4 +212,11 @@ ktib project build --name myimage --tag v1.0 /path/to/project 5. **构建镜像:** ```bash - ktib project build --name myimage --tag latest /path/to/project \ No newline at end of file + ktib project build --name myimage --tag latest /path/to/project + ``` + +6. **一键构建(可选):** + ```bash + ktib make --init --type minimal --name myimage --tag latest /path/to/project + ``` + diff --git a/pkg/project/baseimage_build.go b/pkg/project/baseimage_build.go index 4d6f008..68f22ff 100644 --- a/pkg/project/baseimage_build.go +++ b/pkg/project/baseimage_build.go @@ -12,16 +12,16 @@ package project import ( - "archive/tar" - "context" - "fmt" - "io" - "os" - "path/filepath" - - "gitee.com/openeuler/ktib/pkg/options" - "gitee.com/openeuler/ktib/pkg/utils" - "github.com/containers/buildah/imagebuildah" + "archive/tar" + "context" + "fmt" + "io" + "os" + "path/filepath" + + "gitee.com/openeuler/ktib/pkg/options" + "gitee.com/openeuler/ktib/pkg/utils" + "github.com/containers/buildah/imagebuildah" ) // BuildImage 方法用于构建容器镜像 @@ -33,7 +33,7 @@ func (b *Bootstrap) BuildImage(imageName, tag string) error { } // 创建临时目录用于构建 - buildDir, err := os.MkdirTemp("", "ktib-build-") + buildDir, err := os.MkdirTemp("", "ktib-build-") if err != nil { return fmt.Errorf("创建临时构建目录失败: %v", err) } @@ -47,51 +47,69 @@ func (b *Bootstrap) BuildImage(imageName, tag string) error { // 创建 Dockerfile dockerfilePath := filepath.Join(buildDir, "Dockerfile") - dockerfileContent := `FROM scratch -ADD rootfs.tar / -CMD ["/bin/bash"] -` - if err := os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0644); err != nil { - return fmt.Errorf("创建 Dockerfile 失败: %v", err) - } + projectDockerfilePath := filepath.Join(b.DestinationDir, "dockerfile", "Dockerfile") + if _, err := os.Stat(projectDockerfilePath); err == nil { + srcFile, err := os.Open(projectDockerfilePath) + if err != nil { + return fmt.Errorf("打开项目 Dockerfile 失败: %v", err) + } + defer srcFile.Close() + dstFile, err := os.Create(dockerfilePath) + if err != nil { + return fmt.Errorf("创建构建 Dockerfile 失败: %v", err) + } + defer dstFile.Close() + if _, err = io.Copy(dstFile, srcFile); err != nil { + return fmt.Errorf("复制项目 Dockerfile 失败: %v", err) + } + } else { + cmd := "/bin/bash" + if b.BuildType == "init" { + cmd = "/sbin/init" + } + dockerfileContent := "FROM scratch\nADD rootfs.tar /\nCMD [\"" + cmd + "\"]\n" + if err := os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0644); err != nil { + return fmt.Errorf("创建 Dockerfile 失败: %v", err) + } + } // 复制 files 目录中的文件到构建目录(如果需要) filesDir := filepath.Join(b.DestinationDir, "files") if _, err := os.Stat(filesDir); err == nil { - entries, err := os.ReadDir(filesDir) - if err != nil { - return fmt.Errorf("读取 files 目录失败: %v", err) - } + entries, err := os.ReadDir(filesDir) + if err != nil { + return fmt.Errorf("读取 files 目录失败: %v", err) + } for _, entry := range entries { srcPath := filepath.Join(filesDir, entry.Name()) dstPath := filepath.Join(buildDir, entry.Name()) - if entry.IsDir() { - // 如果是目录,创建对应的目录 - if err := os.MkdirAll(dstPath, 0755); err != nil { - return fmt.Errorf("创建目录 %s 失败: %v", dstPath, err) - } - } else { - // 如果是文件,复制文件内容 - srcFile, err := os.Open(srcPath) - if err != nil { - return fmt.Errorf("打开源文件 %s 失败: %v", srcPath, err) - } - defer srcFile.Close() - - dstFile, err := os.Create(dstPath) - if err != nil { - return fmt.Errorf("创建目标文件 %s 失败: %v", dstPath, err) - } - defer dstFile.Close() - - if _, err = io.Copy(dstFile, srcFile); err != nil { - return fmt.Errorf("复制文件内容失败: %v", err) - } - } - } - } + if entry.IsDir() { + // 如果是目录,创建对应的目录 + if err := os.MkdirAll(dstPath, 0755); err != nil { + return fmt.Errorf("创建目录 %s 失败: %v", dstPath, err) + } + } else { + // 如果是文件,复制文件内容 + srcFile, err := os.Open(srcPath) + if err != nil { + return fmt.Errorf("打开源文件 %s 失败: %v", srcPath, err) + } + defer srcFile.Close() + + dstFile, err := os.Create(dstPath) + if err != nil { + return fmt.Errorf("创建目标文件 %s 失败: %v", dstPath, err) + } + defer dstFile.Close() + + if _, err = io.Copy(dstFile, srcFile); err != nil { + return fmt.Errorf("复制文件内容失败: %v", err) + } + } + } + } // 使用 ktib 内部构建接口构建镜像 imageTag := fmt.Sprintf("%s:%s", imageName, tag) @@ -224,5 +242,5 @@ func createDefaultDockerfile(projectDir string) error { ADD rootfs.tar / CMD ["/bin/bash"] ` - return os.WriteFile(dockerfilePath, []byte(content), 0644) + return os.WriteFile(dockerfilePath, []byte(content), 0644) } diff --git a/pkg/project/bootstrap.go b/pkg/project/bootstrap.go index ff99459..6eeb1f9 100644 --- a/pkg/project/bootstrap.go +++ b/pkg/project/bootstrap.go @@ -46,7 +46,7 @@ type Config struct { // NewBootstrap 创建新的Bootstrap实例 func NewBootstrap(dir string) *Bootstrap { - return &Bootstrap{DestinationDir: dir, BuildType: "baseimage"} + return &Bootstrap{DestinationDir: dir, BuildType: "platform"} } // InitProjectStructure 初始化项目目录结构 @@ -168,8 +168,10 @@ func (b *Bootstrap) AddDockerfile() { os.MkdirAll(dockerfilePath, 0755) // 根据构建类型选择不同的 Dockerfile 模板 - if b.BuildType == "baseimage" { + if b.BuildType == "platform" || b.BuildType == "minimal" || b.BuildType == "micro" { b.initialize(templates.BaseImageDockerfile, "dockerfile/Dockerfile", 0755) + } else if b.BuildType == "init" { + b.initialize(templates.InitImageDockerfile, "dockerfile/Dockerfile", 0755) } else { b.initialize(templates.Dockerfile, "dockerfile/Dockerfile", 0755) } @@ -230,6 +232,11 @@ func (b *Bootstrap) CleanRootfs() error { fmt.Printf("移除不必要的文件失败: %v\n", err) } + // 3. 配置pip并删除pycache + if err := ConfigurePipAndRemovePycache(target, b.BuildType); err != nil { + fmt.Printf("警告: 配置pip或删除pycache失败: %v\n", err) + } + // 2. 解除服务屏蔽 unmaskServicePath := filepath.Join(b.DestinationDir, "files", "unmaskService") fmt.Println("正在解除服务屏蔽") diff --git a/pkg/project/config_rootfs.go b/pkg/project/config_rootfs.go index 20a12cb..d33a425 100644 --- a/pkg/project/config_rootfs.go +++ b/pkg/project/config_rootfs.go @@ -18,6 +18,8 @@ import ( "os/exec" "path/filepath" "strings" + + "gitee.com/openeuler/ktib/pkg/templates" ) var unnecessaryFiles = []string{ @@ -366,3 +368,26 @@ func UnmaskServices(target string, unmaskServicePath string) error { return nil } + +func ConfigurePipAndRemovePycache(target string, imageType string) error { + if imageType == "micro" || imageType == "minimal" { + return nil + } + if os.Geteuid() != 0 { + return fmt.Errorf("需要root权限执行chroot命令") + } + scriptPath := filepath.Join(target, "configure_python.sh") + scriptContent := templates.PythonConfigureScript + if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { + return fmt.Errorf("无法创建Python配置脚本: %v", err) + } + cmd := exec.Command("chroot", target, "/configure_python.sh") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + os.Remove(scriptPath) + if err != nil { + return fmt.Errorf("执行Python配置脚本失败: %v", err) + } + return nil +} diff --git a/pkg/templates/dockerfile.go b/pkg/templates/dockerfile.go index b02803a..84d33a5 100644 --- a/pkg/templates/dockerfile.go +++ b/pkg/templates/dockerfile.go @@ -49,3 +49,10 @@ FROM scratch ADD rootfs.tar / CMD ["/bin/bash"] ` + +// InitImageDockerfile 是用于构建 init 镜像的 Dockerfile 模板 +const InitImageDockerfile = `# {{.ImageName}} init 镜像 +FROM scratch +ADD rootfs.tar / +CMD ["/sbin/init"] +` diff --git a/pkg/templates/python_config.go b/pkg/templates/python_config.go new file mode 100644 index 0000000..ae6a6c8 --- /dev/null +++ b/pkg/templates/python_config.go @@ -0,0 +1,12 @@ +package templates + +const PythonConfigureScript = `#!/bin/bash +set -e +if command -v python3 >/dev/null 2>&1; then + python3 -m ensurepip --upgrade || true + rpm -e --nodeps python-setuptools python-pip-wheel || true + ln -sf /usr/local/bin/pip3 /usr/bin/pip || true + python3 -c 'import pathlib,shutil;[shutil.rmtree(p) for p in pathlib.Path("/").rglob("__pycache__")]' || true +fi +` + diff --git a/pkg/utils/image_type.go b/pkg/utils/image_type.go new file mode 100644 index 0000000..42b0baa --- /dev/null +++ b/pkg/utils/image_type.go @@ -0,0 +1,12 @@ +package utils + +var ValidImageTypes = []string{"micro", "minimal", "platform", "init"} + +func IsValidImageType(t string) bool { + for _, v := range ValidImageTypes { + if t == v { + return true + } + } + return false +} -- Gitee