跳转到主要内容

标签(标签)

资源精选(342) Go开发(108) Go语言(103) Go(99) angular(82) LLM(75) 大语言模型(63) 人工智能(53) 前端开发(50) LangChain(43) golang(43) 机器学习(39) Go工程师(38) Go程序员(38) Go开发者(36) React(33) Go基础(29) Python(24) Vue(22) Web开发(20) Web技术(19) 精选资源(19) 深度学习(19) Java(18) ChatGTP(17) Cookie(16) android(16) 前端框架(13) JavaScript(13) Next.js(12) 安卓(11) typescript(10) 资料精选(10) NLP(10) 第三方Cookie(9) Redwoodjs(9) LLMOps(9) Go语言中级开发(9) 自然语言处理(9) 聊天机器人(9) PostgreSQL(9) 区块链(9) mlops(9) 安全(9) 全栈开发(8) ChatGPT(8) OpenAI(8) Linux(8) AI(8) GraphQL(8) iOS(8) 软件架构(7) Go语言高级开发(7) AWS(7) C++(7) 数据科学(7) whisper(6) Prisma(6) 隐私保护(6) RAG(6) JSON(6) DevOps(6) 数据可视化(6) wasm(6) 计算机视觉(6) 算法(6) Rust(6) 微服务(6) 隐私沙盒(5) FedCM(5) 语音识别(5) Angular开发(5) 快速应用开发(5) 提示工程(5) Agent(5) LLaMA(5) 低代码开发(5) Go测试(5) gorm(5) REST API(5) 推荐系统(5) WebAssembly(5) GameDev(5) CMS(5) CSS(5) machine-learning(5) 机器人(5) 游戏开发(5) Blockchain(5) Web安全(5) Kotlin(5) 低代码平台(5) 机器学习资源(5) Go资源(5) Nodejs(5) PHP(5) Swift(5) 智能体(4) devin(4) Blitz(4) javascript框架(4) Redwood(4) GDPR(4) 生成式人工智能(4) Angular16(4) Alpaca(4) SAML(4) JWT(4) JSON处理(4) Go并发(4) kafka(4) 移动开发(4) 移动应用(4) security(4) 隐私(4) spring-boot(4) 物联网(4) nextjs(4) 网络安全(4) API(4) Ruby(4) 信息安全(4) flutter(4) 专家智能体(3) Chrome(3) CHIPS(3) 3PC(3) SSE(3) 人工智能软件工程师(3) LLM Agent(3) Remix(3) Ubuntu(3) GPT4All(3) 软件开发(3) 问答系统(3) 开发工具(3) 最佳实践(3) RxJS(3) SSR(3) Node.js(3) Dolly(3) 移动应用开发(3) 编程语言(3) 低代码(3) IAM(3) Web框架(3) CORS(3) 基准测试(3) Go语言数据库开发(3) Oauth2(3) 并发(3) 主题(3) Theme(3) earth(3) nginx(3) 软件工程(3) azure(3) keycloak(3) 生产力工具(3) gpt3(3) 工作流(3) C(3) jupyter(3) 认证(3) prometheus(3) GAN(3) Spring(3) 逆向工程(3) 应用安全(3) Docker(3) Django(3) R(3) .NET(3) 大数据(3) Hacking(3) 渗透测试(3) C++资源(3) Mac(3) 微信小程序(3) Python资源(3) JHipster(3) 大型语言模型(2) 语言模型(2) 可穿戴设备(2) JDK(2) SQL(2) Apache(2) Hashicorp Vault(2) Spring Cloud Vault(2) Go语言Web开发(2) Go测试工程师(2) WebSocket(2) 容器化(2) AES(2) 加密(2) 输入验证(2) ORM(2) Fiber(2) Postgres(2) Gorilla Mux(2) Go数据库开发(2) 模块(2) 泛型(2) 指针(2) HTTP(2) PostgreSQL开发(2) Vault(2) K8s(2) Spring boot(2) R语言(2) 深度学习资源(2) 半监督学习(2) semi-supervised-learning(2) architecture(2) 普罗米修斯(2) 嵌入模型(2) productivity(2) 编码(2) Qt(2) 前端(2) Rust语言(2) NeRF(2) 神经辐射场(2) 元宇宙(2) CPP(2) 数据分析(2) spark(2) 流处理(2) Ionic(2) 人体姿势估计(2) human-pose-estimation(2) 视频处理(2) deep-learning(2) kotlin语言(2) kotlin开发(2) burp(2) Chatbot(2) npm(2) quantum(2) OCR(2) 游戏(2) game(2) 内容管理系统(2) MySQL(2) python-books(2) pentest(2) opengl(2) IDE(2) 漏洞赏金(2) Web(2) 知识图谱(2) PyTorch(2) 数据库(2) reverse-engineering(2) 数据工程(2) swift开发(2) rest(2) robotics(2) ios-animation(2) 知识蒸馏(2) 安卓开发(2) nestjs(2) solidity(2) 爬虫(2) 面试(2) 容器(2) C++精选(2) 人工智能资源(2) Machine Learning(2) 备忘单(2) 编程书籍(2) angular资源(2) 速查表(2) cheatsheets(2) SecOps(2) mlops资源(2) R资源(2) DDD(2) 架构设计模式(2) 量化(2) Hacking资源(2) 强化学习(2) flask(2) 设计(2) 性能(2) Sysadmin(2) 系统管理员(2) Java资源(2) 机器学习精选(2) android资源(2) android-UI(2) Mac资源(2) iOS资源(2) Vue资源(2) flutter资源(2) JavaScript精选(2) JavaScript资源(2) Rust开发(2) deeplearning(2) RAD(2)

欢迎各位码农!在本教程中,我们将研究多阶段 Docker 镜像以及如何使用它们来最小化生产 Go 应用程序所需的容器大小。

在本教程结束时,我们将涵盖以下概念:

  • 什么是多阶段 Dockerfile。
  • 我们如何为我们的 Go 应用程序构建简单的多阶段 Dockerfile

Docker 是一种强大的容器化技术,可用于轻松启动隔离且可重现的环境,在其中构建和运行我们的应用程序。它越来越受欢迎,越来越多的云服务提供商提供本地 docker 支持,让您可以轻松部署容器化应用程序,让全世界看到!

注意 - 本教程是我之前的 Go + Docker 教程的后续,可以在此处找到:使用 Docker 容器化您的 Go 应用程序

多阶段 Dockerfile 的需求是什么?


为了了解多阶段 Dockerfile 为何有用,我们将创建一个简单的 Dockerfile,它具有一个阶段来构建和运行我们的应用程序,另一个 Dockerfile 具有构建器阶段和生产阶段。

一旦我们创建了这两个不同的 Dockerfile,我们应该能够比较它们,并希望自己看到多阶段 Dockerfile 比它们更简单的对应物更受欢迎!

因此,在我之前的教程中,我们创建了一个非常简单的 Docker 镜像,我们的 Go 应用程序在其中构建和运行。这个 Dockerfile 看起来像这样:

## We specify the base image we need for our
## go application
FROM golang:1.12.0-alpine3.9
## We create an /app directory within our
## image that will hold our application source
## files
RUN mkdir /app
## We copy everything in the root directory
## into our /app directory
ADD . /app
## We specify that we now wish to execute
## any further commands inside our /app
## directory
WORKDIR /app
## we run go build to compile the binary
## executable of our Go program
RUN go build -o main .
## Our start command which kicks off
## our newly created binary executable
CMD ["/app/main"]


有了这个,我们随后可以使用一个非常简单的 docker build 命令来构建我们的 Docker 镜像:

$ docker build -t go-simple .


这将创建一个镜像并将其存储在我们的本地 docker 镜像存储库中,最终看起来像这样:

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
go-simple               latest              761b9dd5f9a4        4 seconds ago       793MB


您应该希望注意到最后一列指出此镜像的大小为 793MB。对于构建和运行一个非常简单的 Go 应用程序的东西来说,这绝对是巨大的。

在这个镜像中将包含编译和运行我们的 Go 应用程序所需的所有包和依赖项。使用多阶段 dockerfile,我们实际上可以通过将内容分成两个不同的阶段来显着减小这些镜像的大小。

一个简单的多阶段 Dockerfile


使用多阶段 Dockerfile,我们可以将构建和运行 Go 应用程序的任务分成不同的阶段。通常,我们从一个大镜像开始,其中包含编译 Go 应用程序的二进制可执行文件所需的所有必要依赖项、包等。这将被归类为我们的构建器阶段。

然后,我们为我们的运行阶段拍摄一个更轻量级的镜像,其中仅包含运行二进制可执行文件绝对需要的内容。这通常被归类为生产阶段或类似的东西。

## We use the larger image which includes
## all of the dependencies that we need to
## compile our program
FROM bigImageWithEverything AS Builder
RUN go build -o main ./...

## We then define a secondary stage which
## is built off a far smaller image which
## has the absolute bare minimum needed to
## run our binary executable application
FROM LightweightImage AS Production
CMD ["./main"]

通过这种方式,我们受益于一致的构建阶段,并且我们受益于我们的应用程序将在生产环境中运行的绝对微小的镜像。

注意 - 在上面的 psuedo-Dockerfile 中,我使用 AS 关键字为我的镜像添加了别名。这可以帮助我们区分 Dockerfile 的不同阶段,我们可以使用 --target 标志来构建特定阶段。

一个真实的例子


现在我们已经介绍了基本概念,让我们看看我们如何定义一个真正的多阶段 Dockerfile,它将首先编译我们的应用程序,然后在轻量级 Docker alpine 映像中运行我们的应用程序。

出于本教程的目的,我们将从我的新 Go WebSockets 教程中窃取代码,因为这演示了下载依赖项,并且是一个不平凡的示例,因此比标准的 hello world 示例更接近真实的 Go 应用程序。

在项目目录中创建一个名为 main.go 的新文件并添加以下代码:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

// We'll need to define an Upgrader
// this will require a Read and Write buffer size
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

// define a reader which will listen for
// new messages being sent to our WebSocket
// endpoint
func reader(conn *websocket.Conn) {
    for {
        // read in a message
        messageType, p, err := conn.ReadMessage()
        if err != nil {
            log.Println(err)
            return
        }
        // print out that message for clarity
        fmt.Println(string(p))

        if err := conn.WriteMessage(messageType, p); err != nil {
            log.Println(err)
            return
        }

    }
}

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Home Page")
}

func wsEndpoint(w http.ResponseWriter, r *http.Request) {
    upgrader.CheckOrigin = func(r *http.Request) bool { return true }
    fmt.Println(r.Host)

    // upgrade this connection to a WebSocket
    // connection
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
    }
    // listen indefinitely for new messages coming
    // through on our WebSocket connection
    reader(ws)
}

func setupRoutes() {
    http.HandleFunc("/", homePage)
    http.HandleFunc("/ws", wsEndpoint)
}

func main() {
    fmt.Println("Hello World")
    setupRoutes()
    log.Fatal(http.ListenAndServe(":8080", nil))
}

注意 - 我已经使用 go mod init 命令初始化了这个项目以使用 go 模块。这可以使用 Go 版本 1.11 并通过调用 go run ./... 在 docker 容器外部本地运行。

接下来,我们将在与上面的 main.go 文件相同的目录中创建一个 Dockerfile。这将包含一个构建器阶段和一个生产阶段,它们将由两个不同的基础映像构建:

## We'll choose the incredibly lightweight
## Go alpine image to work with
FROM golang:1.11.1 AS builder

## We create an /app directory in which
## we'll put all of our project code
RUN mkdir /app
ADD . /app
WORKDIR /app
## We want to build our application's binary executable
RUN CGO_ENABLED=0 GOOS=linux go build -o main ./...

## the lightweight scratch image we'll
## run our application within
FROM alpine:latest AS production
## We have to copy the output from our
## builder stage to our production stage
COPY --from=builder /app .
## we can then kick off our newly compiled
## binary exectuable!!
CMD ["./main"]

现在我们已经定义了这个多阶段 Dockerfile,我们可以使用标准的 docker build 命令继续构建它:

$ docker build -t go-multi-stage .


现在,当我们将简单镜像的大小与多阶段镜像进行比较时,我们应该会看到大小的显着差异。我们之前的 go-simple 镜像大小约为 800MB,而这个多阶段镜像的大小约为 1/80。

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
go-multi-stage          latest              12dd51472827        24 seconds ago      12.3MB


如果我们想尝试运行它来验证它是否正常工作,我们可以使用以下 docker run 命令来实现:

$ docker run -d -p 8080:8080 go-multi-stage


这将启动以 -d 分离模式运行的 docker 容器,我们应该能够在浏览器中打开 http://localhost:8080 并看到我们的 Go 应用程序将 Hello World 消息返回给我们!

练习 - 复制 Go WebSockets 教程中的 index.html 并在浏览器中打开它,您应该会看到它连接到我们的容器化 Go 应用程序,并且您应该能够使用 docker logs 命令查看日志。

结论


总结一下,在本教程中,我们研究了如何定义一个非常简单的 Dockerfile 来创建一个沉重的 Docker 映像。然后,我们研究了如何通过使用多阶段 Dockerfile 来优化这一点,这给我们留下了令人难以置信的轻量级镜像。

如果您喜欢本教程,或者您有任何意见/反馈/建议,那么我很乐意在下面的建议框中听到它们!

文章链接