Go 脚手架:使用 osbuilder 生成标准化 Dockerfile,并自动构建镜像

本篇文章介绍了如何使用 osbuilder 生成符合 Dockerfile 最佳实践的 Dockerfile,并生成 image Makefile规则构建镜像。

当前企业应用的部署形态基本都是采用云原生的方式来部署。所谓的云原生部署方式,就是指将服务构建成一个容器镜像,并使用 Kubernetes 进行部署。

在将服务构建成容器镜像时,不仅需要编写 Dockerfile,还需要执行 docker命令基于 Dockerfile 和编译产物来构建出最终的镜像。

本文就来详细介绍下 osbuilder 脚手架,是如何生成符合最佳实践、标准的 Dockerfile,并支持 make image命令,来构建出容器镜像的。

一、Dockerfile 最佳实践

在我的公众号文章 Dockerfile 最佳实践,我详细介绍了如何编写符合最佳实践的 Dockerfile。如果你使用 osbuilder 工具生成项目,osbuilder 工具会为你自动生成符合最佳实践的 Dockerfile 文件。

二、使用 osbuilder 工具生成 Dockerfile

首先需要安装 osbuilder 工具。安装命令如下:

$ go install github.com/onexstack/osbuilder/cmd/osbuilder@latest
$ osbuilder version

接下来只需要执行以下几步,便可以生成能够根据需求生成符合最佳实践的 Dockerfile,并能构建出镜像。

步骤 1:编写项目配置文件

项目的配置文件如下(保存在 /tmp/osdemo.yaml 文件中):

scaffold: osbuilder
version: v0.0.16
metadata:
  # 选择二进制文件的部署形式。当前已支持systemd。未来会支持docker、kubernetes,会生产Dockerfile、Kubernetes YAML 等资源
  deploymentMethod: kubernetes
  image:
    # 当指定 deploymentMethod 为 docker、kubernetes 时,构建镜像的地址
    registry: docker.io
    # 指定 Dockerfile 的生成模式。可选的模式有:
    # - none:不生成 Dockerfile。需要自行实现 build/docker/<component_name>/Dockerfile 文件;
    # - runtime-only:仅包含运行时阶段(适合已有外部构建产物),适合本地调试;
    # - multi-stage:多阶段构建(builder + runtime);
    # - combined:同时生成 multi-stage、runtime-only 2 种类型的 Dockerfile:
    #   - multi-stage:Dockerfile 名字为 Dockerfile
    #   - runtime-only:Dockerfile 名字为 Dockerfile.runtime-only
    dockerfileMode: combined
    # 是否采用 distroless 运行时镜像。如果不采用会使用 debian 基础镜像,否则使用 gcr.io/distroless/base-debian12:nonroot
    # - true:采用 gcr.io/distroless/base-debian12:nonroot 基础镜像。生产环境建议设置为 true;
    # - false:采用 debian:bookworm 基础镜像。测试环境建议设置为 fasle;
    distroless: true
  # 控制 Makefile 的生成方式。当前支持以下 3 种:
  # - none:不生成 makefile
  # - structured:生成单个 makefile
  # - unstructured:生成结构化的 makefile
  makefileMode: unstructured
  # 项目创建者名字,用于生成版权信息
  author: 孔令飞
  # 项目创建者邮箱,用于生成版权信息
  email: colin404@foxmail.com
# osbuilder 支持多种应用类型。当前仅支持 Web 服务类型
# 未来会支持:异步任务 Job 类型、命令行工具类型、声明式API服务器类型
webServers:
  - binaryName: os-apiserver
    # Web Server 使用的框架。当前支持 gin、grpc
    # 未来会支持kratos、grpc-gateway、go-zero、kitex、hertz等
    webFramework: grpc
    # Web Server 后端使用的存储类型。当前支持 memory、mysql
    # 未来会支持etcd、redis、sqlite、mongo、postgresql
    storageType: memory
    # 是否添加健康检查接口
    withHealthz: true
    # 是否添加用户默认,开启后,有完整的认证、鉴权流程
    withUser: false
    # 是否生成注册/反注册到腾讯北极星服务中心的代码
    withPolaris: false

上面的配置文件中,我们进行了以下配置:

image:
    # 当指定 deploymentMethod 为 docker、kubernetes 时,构建镜像的地址
    registry: docker.io
    # 指定 Dockerfile 的生成模式。可选的模式有:
    # - none:不生成 Dockerfile。需要自行实现 build/docker/<component_name>/Dockerfile 文件;
    # - runtime-only:仅包含运行时阶段(适合已有外部构建产物),适合本地调试;
    # - multi-stage:多阶段构建(builder + runtime);
    # - combined:同时生成 multi-stage、runtime-only 2 种类型的 Dockerfile:
    #   - multi-stage:Dockerfile 名字为 Dockerfile
    #   - runtime-only:Dockerfile 名字为 Dockerfile.runtime-only
    dockerfileMode: combined
    # 是否采用 distroless 运行时镜像。如果不采用会使用 debian 基础镜像,否则使用 gcr.io/distroless/base-debian12:nonroot
    # - true:采用 gcr.io/distroless/base-debian12:nonroot 基础镜像。生产环境建议设置为 true;
    # - false:采用 debian:bookworm 基础镜像。测试环境建议设置为 fasle;
    distroless: true
  • registry:用来配置镜像的仓库地址;
  • dockerfileMode:用来配置镜像的模式:
  • multi-stage:Dockerfile 名字为 Dockerfile
  • runtime-only:Dockerfile 名字为 Dockerfile.runtime-only
  • none:不生成 Dockerfile。需要自行实现 build/docker/<component_name>/Dockerfile 文件;
  • runtime-only:仅包含运行时阶段(适合已有外部构建产物),适合本地调试;
  • multi-stage:多阶段构建(builder + runtime);
  • combined:同时生成 multi-stage、runtime-only 2 种类型的 Dockerfile:
  • distroless:用来控制是否使用 distroless 基础镜像。生产环境建议使用 distroless 镜像。测试环境为了排障方便,可以不适用 distroless 镜像。
步骤 2:生成项目

执行以下命令生成项目:

$ osbuilder create project -c /tmp/osdemo.yaml osdemo
    步骤 3:生成 REST 资源
    $ cd osdemo
    $ osbuilder create api --kinds cron_job,job -b os-apiserver
    步骤 4:构建 Docker 镜像

    在生成的 osdemo 项目中,已经为 os-apiserver 组件生成了 Dockerfile,位于 build/docker/os-apiserver/目录下:

    $ ls build/docker/os-apiserver
    Dockerfile  Dockerfile.runtime-only
    • Dockerfile 使用多阶段构建的方式来构建镜像,在 builder 镜像中,编译 Go 源码;
    • Dockerfile.runtime-only:在本地编译源码,并被 COPY 到镜像中,适合本地调试。

    这里,我们执行以下命令来构建镜像:

    $ make protoc # 编译 os-apiserver 的 Protobuf 文件
    $ go mod tidy # 下载缺失的依赖包
    $ go generate ./... # 生成代码(依赖注入)
    $ make image RUNTIME_ONLY=1 # 现在本地编译源码,产出二进制文件,再COPY 到镜像中。
    ===========> Building binary os-apiserver v0.0.15 for linux amd64
    ===========> Building docker image os-apiserver v0.0.15 for linux_amd64
    [+] Building 3.3s (16/16) FINISHED
     ...                                                         0.0s
     => => naming to docker.io/osdemo/os-apiserver:v0.0.0

      可以看到,上述命令成功构建出了 docker.io/osdemo/os-apiserver:v0.0.0镜像,执行以下命令运行镜像:

      $ docker run docker.io/osdemo/os-apiserver:v0.0.0
      WARNING: proto: file "auth.proto" is already registered
      See https://protobuf.dev/reference/go/faq#namespace-conflict
      
      I1013 12:18:13.514143       7 server.go:66] "Initializing database connection" type="memory"
      I1013 12:18:13.515584       7 grpc_server.go:62] "Start to listening the incoming requests" protocol="grpc" addr="[::]:6666"

      可以看到,我们可以成功运行镜像。如果你想使用多阶段构建,可以执行以下命令:

      $ make image

        三、osbuilder 工具生成的 Dockerfile 文件

        osbuilder 可以自动生成 4 种类型的 Dockerfile 文件:

        (1) 使用 distroless 镜像,本地编译后 COPY 到镜像中,适合本地测试,Dockerfile 如下:

        # syntax=docker/dockerfile:1.7
        
        # 0) Build args (overridable in CI)
        ARG BUILDER_IMAGE=alpine:3.20
        ARG RUNTIME_IMAGE=gcr.io/distroless/base-debian12:nonroot
        ARG UID=65532
        ARG GID=65532
        
        # 1) Use a lightweight builder image with package manager to fetch tini.
        # builder stage does not compile go source files.
        FROM ${BUILDER_IMAGE} AS builder
        
        # Work directory
        WORKDIR /workspace
        
        # Install minimal tools required to download files securely
        RUN apk add --no-cache curl ca-certificates
        
        # Download the static tini binary (example for amd64); use a different file for other architectures
        # Then make it executable so it can be used as PID 1 in the final image
        RUN curl -fsSL -o /usr/bin/tini https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-amd64 \
         && chmod +x /usr/bin/tini
        
        # 2) Runtime stage
        FROM ${RUNTIME_IMAGE} AS runtime
        
        ARG OS
        ARG ARCH
        ARG UID
        ARG GID
        
        # App directory
        WORKDIR /app
        
        # Copy artifact and set ownership; numeric UID:GID is more robust
        COPY --from=builder --chown=0:0 /usr/bin/tini /usr/bin/tini
        COPY --chown=${UID}:${GID} _output/platforms/${OS}/${ARCH}/os-apiserver /app/os-apiserver
        
        # Security: run as non-root
        USER ${UID}:${GID}
        
        # Use tini as minimal init to handle signals and reap zombies
        ENTRYPOINT ["/usr/bin/tini", "--", "/app/os-apiserver"]

        (2) 使用 distroless 镜像,并使用多阶段构建,Dockerfile 如下:

        # syntax=docker/dockerfile:1.7
        
        # 0) Build args (overridable in CI)
        ARG BUILDER_IMAGE=golang:1.25.0
        ARG RUNTIME_IMAGE=gcr.io/distroless/base-debian12:nonroot
        ARG UID=65532
        ARG GID=65532
        
        # 1) Builder stage
        FROM ${BUILDER_IMAGE} AS builder
        
        ARG OS
        ARG ARCH
        
        # Optional: install build-time tools (as needed)
        # RUN apt-get update && apt-get install -y --no-install-recommends upx && rm -rf /var/lib/apt/lists/*
        
        # Work directory
        WORKDIR /workspace
        
        # Install minimal tools required to download files securely
        RUN apk add --no-cache curl ca-certificates
        
        # Download the static tini binary (example for amd64); use a different file for other architectures
        # Then make it executable so it can be used as PID 1 in the final image
        RUN curl -fsSL -o /usr/bin/tini https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-amd64 \
         && chmod +x /usr/bin/tini
        
        # Use Go modules cache - The most important cache optimization
        # Copy go.mod and go.sum first to leverage Docker layer cache
        COPY go.mod go.sum ./
        
        # Use cache mount to cache Go modules
        RUN --mount=type=cache,target=/go/pkg/mod \
         --mount=type=cache,target=/root/.cache/go-build \
         go mod download
        
        # Copy source code
        COPY . .
        
        # Go build parameters (enable CGO if your project needs static linking)
        ENV CGO_ENABLED=1 GOOS=${OS} GOARCH=${ARCH} GO111MODULE=on GOCACHE=/root/.cache/go-build GOMODCACHE=/go/pkg/mod
        
        # Build with cache mount for make build
        RUN --mount=type=cache,target=/go/pkg/mod \
            --mount=type=cache,target=/root/.cache/go-build \
            make build BINS=os-apiserver
        
        # 2) Runtime stage
        FROM ${RUNTIME_IMAGE} AS runtime
        
        ARG OS
        ARG ARCH
        ARG UID
        ARG GID
        
        # App directory
        WORKDIR /app
        
        # Copy artifacts and set ownership; numeric UID:GID is more robust
        COPY --from=builder --chown=0:0 /tmp/tini /usr/bin/tini
        COPY --from=builder --chown=${UID}:${GID} /workspace/_output/platforms/linux/amd64/os-apiserver /app/os-apiserver
        
        # Security: run as non-root
        USER ${UID}:${GID}
        
        # Use tini as minimal init to handle signals and reap zombies
        ENTRYPOINT ["/usr/bin/tini", "--", "/app/os-apiserver"]

        (3) 不使用 distroless 镜像,本地编译后 COPY 到镜像中,适合本地测试,Dockerfile 如下:

        # syntax=docker/dockerfile:1.7
        
        # 0) Build args (overridable in CI)
        ARG BUILDER_IMAGE=alpine:3.20
        ARG RUNTIME_IMAGE=debian:bookworm
        ARG USER=noroot
        ARG UID=65532
        ARG GID=65532
        
        # 1) Use a lightweight builder image with package manager to fetch tini.
        # builder stage does not compile go source files.
        FROM ${BUILDER_IMAGE} AS builder
        
        # Work directory
        WORKDIR /workspace
        
        # Install minimal tools required to download files securely
        RUN apk add --no-cache curl ca-certificates
        
        # Download the static tini binary (example for amd64); use a different file for other architectures
        # Then make it executable so it can be used as PID 1 in the final image
        RUN curl -fsSL -o /usr/bin/tini https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-amd64 \
         && chmod +x /usr/bin/tini
        
        # 2) Runtime stage
        FROM ${RUNTIME_IMAGE} AS runtime
        
        ARG OS
        ARG ARCH
        ARG USER
        ARG UID
        ARG GID
        
        # App directory
        WORKDIR /app
        
        # Install runtime essentials
        RUN apt-get update \
         && apt-get install -y --no-install-recommends ca-certificates tzdata wget curl telnet \
         && rm -rf /var/lib/apt/lists/*
        
        # Security: create a non-root user (ignore if it already exists)
        # Use || true to prevent build failures if the group/user already exists in the base image
        RUN groupadd -g ${GID} ${USER} 2>/dev/null || true \
         && useradd -u ${UID} -g ${USER} ${USER} 2>/dev/null || true
        
        # Copy artifact and set ownership; numeric UID:GID is more robust
        COPY --from=builder --chown=0:0 /usr/bin/tini /usr/bin/tini
        COPY --chown=${UID}:${GID} _output/platforms/${OS}/${ARCH}/os-apiserver /app/os-apiserver
        
        # Security: run as non-root
        USER ${UID}:${GID}
        
        # Use tini as minimal init to handle signals and reap zombies
        ENTRYPOINT ["/usr/bin/tini", "--", "/app/os-apiserver"]

        (4) 不使用 distroless 镜像,并使用多阶段构建,Dockerfile 如下:

        # syntax=docker/dockerfile:1.7
        
        # 0) Build args (overridable in CI)
        ARG BUILDER_IMAGE=golang:1.25.0
        ARG RUNTIME_IMAGE=debian:bookworm
        ARG USER=noroot
        ARG UID=65532
        ARG GID=65532
        
        # 1) Builder stage
        FROM ${BUILDER_IMAGE} AS builder
        
        ARG OS
        ARG ARCH
        
        # Optional: install build-time tools (as needed)
        # RUN apt-get update && apt-get install -y --no-install-recommends upx && rm -rf /var/lib/apt/lists/*
        
        # Work directory
        WORKDIR /workspace
        
        # Install minimal tools required to download files securely
        RUN apk add --no-cache curl ca-certificates
        
        # Download the static tini binary (example for amd64); use a different file for other architectures
        # Then make it executable so it can be used as PID 1 in the final image
        RUN curl -fsSL -o /usr/bin/tini https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-amd64 \
         && chmod +x /usr/bin/tini
        
        # Use Go modules cache - The most important cache optimization
        # Copy go.mod and go.sum first to leverage Docker layer cache
        COPY go.mod go.sum ./
        
        # Use cache mount to cache Go modules
        RUN --mount=type=cache,target=/go/pkg/mod \
         --mount=type=cache,target=/root/.cache/go-build \
         go mod download
        
        # Copy source code
        COPY . .
        
        # Go build parameters (enable CGO if your project needs static linking)
        ENV CGO_ENABLED=1 GOOS=${OS} GOARCH=${ARCH} GO111MODULE=on GOCACHE=/root/.cache/go-build GOMODCACHE=/go/pkg/mod
        
        # Build with cache mount for make build
        RUN --mount=type=cache,target=/go/pkg/mod \
            --mount=type=cache,target=/root/.cache/go-build \
            make build BINS=os-apiserver
        
        # 2) Runtime stage
        FROM ${RUNTIME_IMAGE} AS runtime
        
        ARG OS
        ARG ARCH
        ARG USER
        ARG UID
        ARG GID
        
        # App directory
        WORKDIR /app
        # Install runtime essentials
        RUN apt-get update \
         && apt-get install -y --no-install-recommends ca-certificates tzdata wget curl telnet \
         && rm -rf /var/lib/apt/lists/*
        
        # Security: create a non-root user (ignore errors if already exists)
        # Use || true to avoid build failures when the group/user already exists in base image
        RUN groupadd -g ${GID} ${USER} 2>/dev/null || true \
         && useradd -u ${UID} -g ${USER} ${USER} 2>/dev/null || true
        
        # Copy artifacts and set ownership; numeric UID:GID is more robust
        COPY --from=builder --chown=0:0 /tmp/tini /usr/bin/tini
        COPY --from=builder --chown=${UID}:${GID} /workspace/_output/platforms/linux/amd64/os-apiserver /app/os-apiserver
        
        # Security: run as non-root
        USER ${UID}:${GID}
        
        # Use tini as minimal init to handle signals and reap zombies
        ENTRYPOINT ["/usr/bin/tini", "--", "/app/os-apiserver"]

        四、总结

        本篇文章介绍了如何使用 osbuilder 生成符合 Dockerfile 最佳实践的 Dockerfile,并生成 image Makefile规则构建镜像。

        AI大模型学习福利

        作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

        一、全套AGI大模型学习路线

        AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

        因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

        二、640套AI大模型报告合集

        这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

        因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

        三、AI大模型经典PDF籍

        随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


        因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

        四、AI大模型商业化落地方案

        因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

        作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

        当前余额3.43前往充值 >
        需支付:10.00
        成就一亿技术人!
        领取后你会自动成为博主和红包主的粉丝 规则
        hope_wisdom
        发出的红包
        实付
        使用余额支付
        点击重新获取
        扫码支付
        钱包余额 0

        抵扣说明:

        1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
        2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

        余额充值