谁从未怀疑过自己是否为应用程序创建了正确的 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
很少有人知道我们不需要在生产环境中运行“完整”的镜像。例如,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,但是你用“这两张图片”做什么?首先,我使用图像作为构建器,这是我们创建应用程序可执行文件的地方:
第一阶段(构建阶段):
然后,我使用一个较小的映像来运行我们在第一步中生成的可执行文件:
第二阶段(最后阶段):
如果我们想确认到底发生了什么,在 Docker Desktop 中可以分析图像的层次结构。在里面我们可以看到他使用的东西:
现在我们还可以分析刚刚生成的图像的大小,在本例中是 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 个支柱相关:
好的,但是如果我使用相同的 SDK 版本(在本例中为 golang:1.22-alpine)怎么办?我的应用程序有多大?
REPOSITORY TAG IMAGE ID CREATED SIZE url-shortener golang-alpine d615d75c3aff 25 minutes ago 251MB
好吧,在这种情况下,我们最终得到了大约 250mb 的图像...而与阿尔卑斯山相比,我们纯粹去了大约 30mb,这已经是一个很大的差异了。还能进一步改进吗?
答案是肯定的,让我们详细了解一下
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:
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 ?
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:
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.
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 :)
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.
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.
通过这种方式,我们将进行主动预防,在将映像部署到生产环境之前识别并纠正漏洞,帮助防止可能的安全漏洞,并且我们还可以获得运营效率,这是什么意思?我们可以自动化漏洞分析,让 DevOps 或安全团队专注于纠正措施,而不是手动调查。
通过我们探索的实践,您现在可以使用多阶段构建等技术为 Go 应用程序构建 Docker 镜像,这些技术可以减少镜像大小,选择 alpine 或 scrap 等基础镜像来提高安全性和效率,通过使用 Docker Scout 来监控漏洞,您可以确保您的应用程序高效、安全地运行。
这些做法不仅提高了技术性能,还为您的日常生活和公司带来直接的效益,节省时间和资源。
因此,下次构建 Docker 映像时,请记住这些策略。应用它们并观察结果。 ?
以上是Go + Docker:如何为 Golang 应用程序创建最佳的 Docker 镜像的详细内容。更多信息请关注PHP中文网其他相关文章!