diff --git a/actions/dockerfile/1.0/Dockerfile b/actions/dockerfile/1.0/Dockerfile index 533aa28b..bebbaac9 100644 --- a/actions/dockerfile/1.0/Dockerfile +++ b/actions/dockerfile/1.0/Dockerfile @@ -1,13 +1,16 @@ -FROM registry.erda.cloud/erda-actions/custom-script:2.0 AS builder +FROM --platform=$TARGETPLATFORM registry.erda.cloud/retag/buildkit:v0.11.3 as buildkit +FROM --platform=$TARGETPLATFORM registry.erda.cloud/erda-x/golang:1.22 AS builder -ENV CGO_ENABLED 0 - -COPY . /go/src/github.com/erda-project/erda-actions WORKDIR /go/src/github.com/erda-project/erda-actions +ENV CGO_ENABLED=0 + +COPY . . + RUN go build -o /opt/action/run github.com/erda-project/erda-actions/actions/dockerfile/1.0/internal/cmd -FROM registry.erda.cloud/erda-actions/custom-script:2.0 +FROM --platform=$TARGETPLATFORM registry.erda.cloud/erda-x/debian-bookworm:12 COPY --from=builder /opt/action/run /opt/action/run +COPY --from=buildkit /usr/bin/buildctl /usr/bin/buildctl RUN chmod +x /opt/action/run diff --git a/actions/dockerfile/1.0/internal/pkg/build/buildkit.go b/actions/dockerfile/1.0/internal/pkg/build/buildkit.go new file mode 100644 index 00000000..bf26bd05 --- /dev/null +++ b/actions/dockerfile/1.0/internal/pkg/build/buildkit.go @@ -0,0 +1,91 @@ +package build + +import ( + "fmt" + "os" + "os/exec" + "path" + "path/filepath" + "strconv" + + "github.com/erda-project/erda-actions/actions/dockerfile/1.0/internal/pkg/conf" + pkgconf "github.com/erda-project/erda-actions/pkg/envconf" + "github.com/erda-project/erda-infra/pkg/strutil" +) + +const ( + defaultCaCertPath = "/.buildkit/ca.pem" + defaultCertPath = "/.buildkit/cert.pem" + defaultCertKeyPath = "/.buildkit/key.pem" +) + +type buildkit struct { + config *conf.Conf + args []string +} + +func NewBuildkit(c *conf.Conf) Builder { + args := []string{ + "--addr", c.BuildkitdAddr, + fmt.Sprintf("--tlscacert=%s", defaultCaCertPath), + fmt.Sprintf("--tlscert=%s", defaultCertPath), + fmt.Sprintf("--tlskey=%s", defaultCertKeyPath), + "build", + "--frontend", "dockerfile.v0", + "--local", "context=" + c.Context, + "--local", "dockerfile=" + resolveDockerfileDir(c), + "--opt", fmt.Sprintf("platform=%s", pkgconf.GetTargetPlatforms()), + } + + return &buildkit{ + config: c, + args: args, + } +} + +func (b *buildkit) Build(p *Params, o *OutPut) error { + // generate command from action params provided + b.appendBuildArgs(p.Args) + b.appendBuildContexts(p.BuildContext) + + // set output + b.args = append(b.args, + "--output", + fmt.Sprintf("type=image,name=%s,push=%s", o.Image, strconv.FormatBool(o.Push)), + ) + + buildCmd := exec.Command("buildctl", b.args...) + fmt.Println(strutil.Join(buildCmd.Args, " ", false)) + + buildCmd.Dir = b.config.WorkDir + buildCmd.Stdout = os.Stdout + buildCmd.Stderr = os.Stderr + + if err := buildCmd.Run(); err != nil { + return fmt.Errorf("failed to build image: %v", err) + } + + fmt.Fprintf(os.Stdout, "Successfully built and pushed image: %s\n", o.Image) + return nil +} + +func resolveDockerfileDir(c *conf.Conf) string { + if path.IsAbs(c.Path) { + return filepath.Dir(c.Path) + } + return filepath.Dir(path.Join(c.Context, c.Path)) +} + +func (b *buildkit) appendBuildArgs(args map[string]string) { + for k, v := range args { + b.args = append(b.args, "--opt", fmt.Sprintf("build-arg:%s=%s", k, v)) + } +} + +func (b *buildkit) appendBuildContexts(buildContexts map[string]string) { + for k, v := range buildContexts { + b.args = append(b.args, + "--local", fmt.Sprintf("%s=%s", k, v), + "--opt", fmt.Sprintf("context:%s=local:%s", k, k)) + } +} diff --git a/actions/dockerfile/1.0/internal/pkg/build/docker.go b/actions/dockerfile/1.0/internal/pkg/build/docker.go new file mode 100644 index 00000000..949c583a --- /dev/null +++ b/actions/dockerfile/1.0/internal/pkg/build/docker.go @@ -0,0 +1,77 @@ +package build + +import ( + "fmt" + "os" + "os/exec" + "strconv" + + "github.com/erda-project/erda-actions/actions/dockerfile/1.0/internal/pkg/conf" + "github.com/erda-project/erda-actions/pkg/docker" + "github.com/erda-project/erda/apistructs" +) + +type dockerBuilder struct { + config *conf.Conf + args []string +} + +func NewDocker(c *conf.Conf) Builder { + args := []string{ + "build", + ".", // set current dir is build context + "-f", c.Path, + } + + return &dockerBuilder{ + config: c, + args: args, + } +} + +func (d *dockerBuilder) Build(p *Params, o *OutPut) error { + // generate command from action params provided + d.appendBuildArgs(p.Args) + d.appendBuildContexts(p.BuildContext) + + // set resource limit + d.args = append(d.args, + "--cpu-quota", strconv.FormatFloat(d.config.CPU*100000, 'f', 0, 64), + "--memory", strconv.FormatInt(int64(d.config.Memory*apistructs.MB), 10), + ) + + // generate build command + buildCmd := exec.Command("docker", d.args...) + fmt.Fprintf(os.Stdout, "Docker build command: %v\n", buildCmd.Args) + + buildCmd.Stdout = os.Stdout + buildCmd.Stderr = os.Stderr + buildCmd.Dir = d.config.WorkDir + + if err := buildCmd.Run(); err != nil { + return fmt.Errorf("failed to build docker image: %v", err) + } + + if o.Push { + if err := docker.PushByCmd(o.Image, ""); err != nil { + return fmt.Errorf("failed to push docker image: %v", err) + } + } + + fmt.Fprintf(os.Stdout, "Successfully built and pushed image: %s\n", o.Image) + return nil +} + +// appendBuildArgs +func (d *dockerBuilder) appendBuildArgs(args map[string]string) { + for k, v := range args { + d.args = append(d.args, "--build-arg", fmt.Sprintf("%s=%s", k, v)) + } +} + +// appendBuildContexts +func (d *dockerBuilder) appendBuildContexts(contexts map[string]string) { + for k, v := range contexts { + d.args = append(d.args, "--build-context", fmt.Sprintf("%s=%s", k, v)) + } +} diff --git a/actions/dockerfile/1.0/internal/pkg/build/execute.go b/actions/dockerfile/1.0/internal/pkg/build/execute.go index bd6058bb..80e83f08 100644 --- a/actions/dockerfile/1.0/internal/pkg/build/execute.go +++ b/actions/dockerfile/1.0/internal/pkg/build/execute.go @@ -3,21 +3,16 @@ package build import ( "encoding/json" "fmt" - "io/ioutil" "os" - "os/exec" - "path" "path/filepath" "strconv" "strings" "time" - pkgconf "github.com/erda-project/erda-actions/pkg/envconf" "github.com/erda-project/erda/apistructs" "github.com/erda-project/erda/pkg/envconf" "github.com/erda-project/erda/pkg/filehelper" "github.com/erda-project/erda/pkg/metadata" - "github.com/erda-project/erda/pkg/strutil" "github.com/labstack/gommon/random" "github.com/pkg/errors" @@ -27,6 +22,20 @@ import ( "github.com/erda-project/erda-actions/pkg/pack" ) +type Builder interface { + Build(p *Params, o *OutPut) error +} + +type OutPut struct { + Image string + Push bool +} + +type Params struct { + Args map[string]string + BuildContext map[string]string +} + // Execute 自定义 dockerfile 构建应用镜像 func Execute() error { var cfg conf.Conf @@ -72,12 +81,12 @@ func packAndPushImage(cfg conf.Conf) error { return err } - originalDockerfileContent, err := ioutil.ReadFile(cfg.Path) + originalDockerfileContent, err := os.ReadFile(cfg.Path) if err != nil { return err } newDockerfileContent := dockerfile.ReplaceOrInsertBuildArgToDockerfile(originalDockerfileContent, cfg.BuildArgs) - if err = ioutil.WriteFile(cfg.Path, newDockerfileContent, 0644); err != nil { + if err = os.WriteFile(cfg.Path, newDockerfileContent, 0644); err != nil { return err } } @@ -91,16 +100,37 @@ func packAndPushImage(cfg conf.Conf) error { "DICE_WORKSPACE": cfg.DiceWorkspace, } - if cfg.BuildkitEnable == "true" { - if err := packWithBuildkit(cfg, repo, buildArgs); err != nil { - fmt.Fprintf(os.Stdout, "failed to pack with buildkit: %v\n", err) - return err - } + buildContext := cfg.BuildContext + if buildContext == nil { + buildContext = make(map[string]string) + } + + var builder Builder + + isBuildkitEnable, err := strconv.ParseBool(cfg.BuildkitEnable) + if err != nil { + fmt.Fprintf(os.Stdout, "unparsed builder keyword: %v\n", err) + return err + } + + if isBuildkitEnable { + builder = NewBuildkit(&cfg) } else { - if err := packWithDocker(cfg, repo, buildArgs); err != nil { - fmt.Fprintf(os.Stdout, "failed to pack with docker: %v\n", err) - return err - } + builder = NewDocker(&cfg) + } + + if err := builder.Build( + &Params{ + Args: buildArgs, + BuildContext: buildContext, + }, + &OutPut{ + Image: repo, + Push: true, // push default now. + }, + ); err != nil { + fmt.Fprintf(os.Stdout, "failed to build: %v\n", err) + return err } // upload metadata @@ -125,87 +155,6 @@ func packAndPushImage(cfg conf.Conf) error { return nil } -func packWithDocker(cfg conf.Conf, repo string, args map[string]string) error { - argsSlice := make([]string, 0) - - for k, v := range args { - argsSlice = append(argsSlice, "--build-arg", fmt.Sprintf("%s=%s", k, v)) - } - - buildCmdArgs := []string{"build"} - buildCmdArgs = append(buildCmdArgs, argsSlice...) - buildCmdArgs = append(buildCmdArgs, - "--cpu-quota", strconv.FormatFloat(float64(cfg.CPU*100000), 'f', 0, 64), - "--memory", strconv.FormatInt(int64(cfg.Memory*apistructs.MB), 10), - "-t", repo, - "-f", cfg.Path, ".") - - packCmd := exec.Command("docker", buildCmdArgs...) - - fmt.Fprintf(os.Stdout, "packCmd: %v\n", packCmd.Args) - packCmd.Stdout = os.Stdout - packCmd.Stderr = os.Stderr - if err := packCmd.Run(); err != nil { - return err - } - fmt.Fprintf(os.Stdout, "successfully build app image: %s\n", repo) - - // docker push 业务镜像至集群 registry - if err := docker.PushByCmd(repo, ""); err != nil { - return err - } - - return nil -} - -func packWithBuildkit(cfg conf.Conf, repo string, args map[string]string) error { - argsSlice := make([]string, 0) - - for k, v := range args { - argsSlice = append(argsSlice, "--opt", fmt.Sprintf("build-arg:%s=%s", k, v)) - } - - buildCmdArgs := []string{ - "--addr", cfg.BuildkitdAddr, - "--tlscacert=/.buildkit/ca.pem", - "--tlscert=/.buildkit/cert.pem", - "--tlskey=/.buildkit/key.pem", - "build", - "--frontend", "dockerfile.v0", - } - - // append args, e.g. --opt k=v - buildCmdArgs = append(buildCmdArgs, argsSlice...) - - // Get dockerfile dir - var dfDir string - if path.IsAbs(cfg.Path) { - dfDir = filepath.Dir(cfg.Path) - } else { - dfDir = filepath.Dir(path.Join(cfg.Context, cfg.Path)) - } - - // append build source and output param. - buildCmdArgs = append(buildCmdArgs, - "--local", "context="+cfg.Context, - "--local", "dockerfile="+dfDir, - "--opt", fmt.Sprintf("platform=%s", pkgconf.GetTargetPlatforms()), - "--output", "type=image,name="+repo+",push=true", - ) - - buildkitCmd := exec.Command("buildctl", buildCmdArgs...) - fmt.Println(strutil.Join(buildkitCmd.Args, " ", false)) - - buildkitCmd.Dir = cfg.WorkDir - buildkitCmd.Stdout = os.Stdout - buildkitCmd.Stderr = os.Stderr - if err := buildkitCmd.Run(); err != nil { - return err - } - - return nil -} - // 生成业务镜像名称 func getRepo(cfg conf.Conf) string { // registry url diff --git a/actions/dockerfile/1.0/internal/pkg/conf/conf.go b/actions/dockerfile/1.0/internal/pkg/conf/conf.go index 7eca6d2e..6b4021c3 100644 --- a/actions/dockerfile/1.0/internal/pkg/conf/conf.go +++ b/actions/dockerfile/1.0/internal/pkg/conf/conf.go @@ -9,9 +9,10 @@ type Conf struct { Path string `env:"ACTION_PATH" required:"true"` BuildArgsStr string `env:"ACTION_BUILD_ARGS"` // 用于渲染 dockerfile BuildArgs map[string]string - Service string `env:"ACTION_SERVICE"` // TODO deprecated - Image *DockerImage `env:"ACTION_IMAGE"` - Registry *DockerRegistry `env:"ACTION_REGISTRY"` + BuildContext map[string]string `env:"ACTION_BUILD_CONTEXT"` + Service string `env:"ACTION_SERVICE"` // TODO deprecated + Image *DockerImage `env:"ACTION_IMAGE"` + Registry *DockerRegistry `env:"ACTION_REGISTRY"` // pipeline 注入,镜像生成时使用 TaskName string `env:"PIPELINE_TASK_NAME" default:"unknown"` ProjectAppAbbr string `env:"DICE_PROJECT_APPLICATION"` // 用于生成用户镜像repo diff --git a/actions/java/1.0/comp/openjdk/Dockerfile b/actions/java/1.0/comp/openjdk/Dockerfile index a6b285aa..e95e2910 100644 --- a/actions/java/1.0/comp/openjdk/Dockerfile +++ b/actions/java/1.0/comp/openjdk/Dockerfile @@ -1,8 +1,8 @@ +# source: dockerfiles/pyroscope/java/Dockerfile +# /app/pyroscope.jar natively supports amd64/arm64 arch. FROM registry.erda.cloud/retag/pyroscope-java:v0.11.5 as pyroscope-java -FROM registry.erda.cloud/erda-x/openjdk:8_11 - -ARG CONTAINER_VERSION=v8 -ENV CONTAINER_VERSION ${CONTAINER_VERSION} +# 可选: v1.8.0.242 +FROM --platform=$TARGETPLATFORM registry.erda.cloud/erda-x/openjdk:8_11 ARG TARGET ARG MONITOR_AGENT=true @@ -10,6 +10,8 @@ ARG SCRIPT_ARGS ENV SCRIPT_ARGS ${SCRIPT_ARGS} +RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone + COPY comp/openjdk/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh @@ -29,6 +31,7 @@ RUN \ COPY --from=pyroscope-java /app /opt/pyroscope +# copy target RUN mkdir -p /app COPY ${TARGET} /app/ diff --git a/actions/java/1.0/comp/tomcat/Dockerfile b/actions/java/1.0/comp/tomcat/Dockerfile index 573b8573..50bc2984 100644 --- a/actions/java/1.0/comp/tomcat/Dockerfile +++ b/actions/java/1.0/comp/tomcat/Dockerfile @@ -1,9 +1,11 @@ +# 可选: v8.5.43-jdk8, v7.0.96-jdk8 +ARG CONTAINER_VERSION=v8.5.43-jdk8 + +# source: dockerfiles/pyroscope/java/Dockerfile +# /app/pyroscope.jar natively supports amd64/arm64 arch. FROM registry.erda.cloud/retag/pyroscope-java:v0.11.5 as pyroscope-java FROM registry.erda.cloud/erda-x/tomcat:8.5 -ARG CONTAINER_VERSION=v8 -ENV CONTAINER_VERSION ${CONTAINER_VERSION} - ARG TARGET ARG MONITOR_AGENT=true ARG SCRIPT_ARGS @@ -11,6 +13,8 @@ ARG WEB_PATH ENV SCRIPT_ARGS ${SCRIPT_ARGS} +RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone + COPY comp/tomcat/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh @@ -31,6 +35,7 @@ WORKDIR / RUN cd ${CATALINA_HOME}/webapps/ && rm -fr * +# biz war COPY ${TARGET}/app.war ${CATALINA_HOME}/webapps/${WEB_PATH}.war CMD ["/entrypoint.sh"]