首页  >  文章  >  后端开发  >  Go + Docker:如何为 Golang 应用程序创建最佳的 Docker 镜像

Go + Docker:如何为 Golang 应用程序创建最佳的 Docker 镜像

WBOY
WBOY原创
2024-08-14 22:49:02430浏览

谁从未怀疑过自己是否为应用程序创建了正确的 Docker 镜像?嗯,这个问题我已经问过好几次了,但我几乎总是不知道自己这样做是对还是错。

因此,在本文中,我们将探索为 Go 应用程序创建高效且优化的 Docker 镜像的高级实践,我们将比较不同的方法,例如使用 alpine 和 scrap 基础镜像,并通过代码讨论每种方法的优点。示例和性能分析。

项目结构

首先,让我们为容器化 Go 应用程序建立一个典型的项目结构。

举个例子,我正在使用这个应用程序,它是一个 URL 缩短器:

url_shortener
├── cmd
│   └── main.go
├── internal
│   ├── monitoring
│   │   └── prometheus.go
│   ├── server
│   │   └── server.go
│   └── shortener
│       ├── model.go
│       ├── repository.go
│       ├── service.go
│       └── service_test.go
├── pkg
│   ├── api
│   │   └── shortener
│   │       ├── handler.go
│   │       └── handler_integration_test.go
│   └── utils
│       └── base62
│           ├── hash.go
│           └── hash_test.go
├── Dockerfile
├── Dockerfile.alpine
├── Dockerfile.golang
├── README.md
├── compose.yml
├── go.mod
├── go.sum
└── prometheus.yml

1. 编写Dockerfile

很少有人知道我们不需要在生产环境中运行“完整”的镜像。例如,Ubuntu 具有我们语言的所有软件包、源代码、扩展和 SDK。我们可以简单地使用 SDK 在系统中构建我们的应用程序,然后将构建复制到仅运行该构建的较小的优化映像。这就是多阶段发挥作用的地方。

多阶段构建

在 Dockerfile 中,您可以定义多个构建阶段,每个阶段都以 FROM 语句开始。第一阶段可用于编译代码、安装依赖项、运行测试等。在后续阶段,您可以仅将必要的工件(例如编译的二进制文件)复制到最终映像,丢弃运行应用程序不需要的所有内容。

# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o url-shortener ./cmd

# Final stage
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/url-shortener .
CMD ["./url-shortener"]

该死的Rafa,但是你用“这两张图片”做什么?首先,我使用图像作为构建器,这是我们创建应用程序可执行文件的地方:

第一阶段(构建阶段):

  • 使用 golang:1.22-alpine 作为基础镜像。
  • 将源代码复制到容器中。
  • 下载依赖项并编译 Go 应用程序二进制文件。

然后,我使用一个较小的映像来运行我们在第一步中生成的可执行文件:

第二阶段(最后阶段):

  • 使用 alpine:latest 作为基础镜像,它要小得多,并且只需要运行二进制文件所需的工具。
  • 将编译后的二进制文件从前一阶段(构建器)复制到新映像。
  • 定义应用程序执行命令。

如果我们想确认到底发生了什么,在 Docker Desktop 中可以分析图像的层次结构。在里面我们可以看到他使用的东西:

Go + Docker: Como criar as melhores imagens Docker para aplicações Golang

现在我们还可以分析刚刚生成的图像的大小,在本例中是 url-shortener:alpine,约为 30mb:

$ docker images
REPOSITORY            TAG         IMAGE ID       CREATED              SIZE
url-shortener                 alpine          aa99d6a2c028   3 minutes ago    29.9MB

但什么是阿尔卑斯山?

Alpine 是一个极简、安全、轻量级的 Linux 发行版,因其高效和简单而广泛应用于容器环境中。它为构建可扩展的应用程序提供了坚实的基础,而无需其他较重的 Linux 发行版的开销。

在我们的应用程序中使用 Alpine 的一些优势主要与以下 3 个支柱相关:

  • 灵活性:alpine 镜像(~5MB)很小,并且包含一个 apk 包管理器,允许您安装其他所需的依赖项。
  • 兼容性:支持动态库,适合依赖外部库的应用。
  • 安全性:定期更新,alpine 包含安全补丁,降低漏洞风险。

好的,但是如果我使用相同的 SDK 版本(在本例中为 golang:1.22-alpine)怎么办?我的应用程序有多大?

REPOSITORY                    TAG             IMAGE ID       CREATED         SIZE
url-shortener                 golang-alpine   d615d75c3aff   25 minutes ago   251MB

好吧,在这种情况下,我们最终得到了大约 250mb 的图像...而与阿尔卑斯山相比,我们纯粹去了大约 30mb,这已经是一个很大的差异了。还能进一步改进吗?

答案是肯定的,让我们详细了解一下

2. 高级优化

2.1 划痕

Scratch 是 Docker 中一个特殊且非常简约的镜像。它实际上是您可以使用的最简单、最空的基础映像。它绝对不包含任何内容:没有操作系统、没有库、没有工具——它实际上是一个空容器。

Essa abordagem minimalista traz benefícios significativos, especialmente em termos de segurança. Ao usar Scratch, você minimiza drasticamente a superfície de ataque, já que não há pacotes ou ferramentas adicionais que possam introduzir vulnerabilidades. Seu contêiner contém apenas o essencial para a execução do aplicativo, garantindo um ambiente imutável e previsível em qualquer situação.

# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o url-shortener ./cmd

# Final stage with Scratch
FROM scratch
WORKDIR /app
COPY --from=builder /app/url-shortener .
CMD ["./url-shortener"]

E o resultado após criar as 3 imagens, nosso ranking de menor imagem ficou assim:

  1. url-shortener:scratch - 21.1MB
  2. url-shortener:alpine - 29.9MB
  3. url-shortener:golang-alpine - 251MB

O Scratch conseguiu baixar mais alguns megas no tamanho final de nossa aplicação. Isso impacta no tamanho de arquivos transferidos pela rede, hoje algumas empresas cobram pela banda que trafegamos dentro dos servidores, e também pode influenciar em nosso Horizontal Scaling da aplicação.

Deixei os 3 Dockerfiles dentro do repositório do github caso você queira testar em seu próprio pc ?

Quando devo usar o scratch

A resposta mais tranquila para essa é "quase sempre", uma dica de cara é: ele vai muito bem com linguagens como Go, Rust, ou C/C++. Mas qui estão alguns pontos para levar em consideração na hora de escolher se deve ou não usar o scratch:

  • Aplicações Statically Linked: Se sua aplicação não depende de bibliotecas dinâmicas e pode ser compilada de forma estática, Scratch é uma excelente escolha.
  • Segurança: Quando a segurança é uma prioridade e você quer minimizar a quantidade de software no contêiner.
  • Eficiência: Para criar imagens Docker extremamente pequenas e eficientes.

2.2 Reduzindo o Tempo de Build (Cache)

Usar o cache do Docker para otimizar o tempo de build é uma técnica essencial para evitar recompilar ou baixar dependências desnecessariamente em cada build. O Docker armazena em cache as camadas de cada etapa do Dockerfile, reutilizando-as sempre que possível.

Em projetos Go, baixar dependências com go mod download pode ser um processo demorado, especialmente se houver muitas dependências. Se você recompilar todas as dependências em cada build, isso aumenta significativamente o tempo de build.

Como arrumar isso?

Ao copiar apenas os arquivos go.mod e go.sum em uma etapa separada antes de copiar o código-fonte completo, você permite que o Docker use o cache dessa etapa se os arquivos go.mod e go.sum não tiverem mudado. Veja como fica nosso Docker file com as mudanças:

# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o url-shortener ./cmd

# Final stage
FROM scratch
WORKDIR /app
COPY --from=builder /app/url-shortener .
CMD ["./url-shortener"]

Só de fazer esta pequena mudança já ganhamos dois pontos bem interessantes quando se trata de desenvolvimento de software, que são:
Menor tempo de build: Se não houver alterações nos arquivos go.mod e go.sum, o Docker reutiliza o cache, evitando o download das dependências e economizando tempo.
Eficiência no CI/CD: Em pipelines de integração contínua, essa técnica reduz o tempo de execução dos pipelines, aumentando a eficiência do desenvolvimento e entrega.

Então bora usar isso a nosso favor no dia-a-dia :)

2.3 Melhorando a Segurança (Docker Scout)

Docker Scout é uma ferramenta da Docker integrada ao Docker Desktop que analisa suas imagens para identificar vulnerabilidades de segurança. Ele fornece insights sobre as dependências presentes em suas imagens e alerta sobre possíveis problemas, permitindo que você tome medidas corretivas antes de implantar suas aplicações.

Por que é importante? Manter suas imagens Docker seguras é fundamental para proteger suas aplicações contra ataques e exploração de vulnerabilidades. O Docker Scout automatiza o processo de análise, tornando mais fácil manter suas imagens seguras e atualizadas.

Como funciona?

O Scout funciona praticamente com 2 passos, ele examina a imagem Docker, mapeia todas as dependências incluídas na imagem e verifica essas dependências em uma base de dados de vulnerabilidades conhecidas. Por fim, classifica as vulnerabilidades encontradas por severidade e fornece recomendações para corrigir ou mitigar os problemas.

实际示例:在 Docker Desktop 上使用 Docker Scout

  1. 访问 Docker 桌面: 打开 Docker Desktop 并转到“Images”选项卡。在这里您将看到本地所有图像的列表。在此示例中,我使用了 postgres:16-alpine 映像,因为它包含我们可以用作示例的漏洞。 Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  2. 执行漏洞分析: 选择您要分析的图像。 Docker Scout 将自动显示所选镜像中的已知漏洞。 您将在每个图像旁边看到一个安全状态图标,指示是否存在需要解决的漏洞。 Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  3. 查看漏洞详细信息: 单击图像可查看已发现漏洞的详细仪表板。这包括受影响的软件包版本、CVE 描述和严重性。 您还可以查看扫描和更改的历史记录,帮助您跟踪图像安全性随时间的演变情况。 Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  4. 应用修复: 根据 Docker Scout 建议,您可以决定更新软件包、使用更安全的基础重建映像或采取其他缓解措施。 修复可以直接应用于您的 Dockerfile 或 CI/CD 管道。 Go + Docker: Como criar as melhores imagens Docker para aplicações Golang

通过这种方式,我们将进行主动预防,在将映像部署到生产环境之前识别并纠正漏洞,帮助防止可能的安全漏洞,并且我们还可以获得运营效率,这是什么意思?我们可以自动化漏洞分析,让 DevOps 或安全团队专注于纠正措施,而不是手动调查。

结论

通过我们探索的实践,您现在可以使用多阶段构建等技术为 Go 应用程序构建 Docker 镜像,这些技术可以减少镜像大小,选择 alpine 或 scrap 等基础镜像来提高安全性和效率,通过使用 Docker Scout 来监控漏洞,您可以确保您的应用程序高效、安全地运行。

这些做法不仅提高了技术性能,还为您的日常生活和公司带来直接的效益,节省时间和资源。

因此,下次构建 Docker 映像时,请记住这些策略。应用它们并观察结果。 ?

以上是Go + Docker:如何为 Golang 应用程序创建最佳的 Docker 镜像的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn